Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f2d0e72f2 | ||
|
|
c9552f9785 | ||
|
|
444ff3e2d1 | ||
|
|
7d56c8a381 | ||
|
|
da051f8d8f | ||
|
|
81e6bc5833 | ||
|
|
ca5ea827b2 | ||
|
|
b700fff9ac | ||
|
|
81741364a7 | ||
|
|
faebc42f0d | ||
|
|
63e11ca377 | ||
|
|
54a2215e04 | ||
|
|
5edc3869cd | ||
|
|
a772d660f3 | ||
|
|
3c504cc85d | ||
|
|
0a2c16db0c | ||
|
|
2bd84c8d5a | ||
|
|
7cd668efb7 | ||
|
|
a6265c3089 | ||
|
|
a2300c1a96 | ||
|
|
3dae3f9173 | ||
|
|
63c420d5d8 | ||
|
|
a94c6bb581 | ||
|
|
9fe0044a65 | ||
|
|
c05ce5eff4 | ||
|
|
a019103ed5 | ||
|
|
50e55f4fca | ||
|
|
0706683690 | ||
|
|
9cdb77bafa | ||
|
|
b476c54825 | ||
|
|
8b2e541b1d | ||
|
|
931531e799 | ||
|
|
1695d69aa3 | ||
|
|
217bd3bed3 | ||
|
|
384e8794bf | ||
|
|
61b398a89a | ||
|
|
ba78fd0dc5 | ||
|
|
43969a773e | ||
|
|
6e1eabbe0f | ||
|
|
b99d1e5dea | ||
|
|
6ce566aea5 | ||
|
|
a4209961e2 | ||
|
|
181ff3f2bf | ||
|
|
b85b522643 | ||
|
|
5158e264c0 | ||
|
|
fd67da0527 | ||
|
|
7bdec8457f | ||
|
|
bc75c9a89f | ||
|
|
c2e8734e85 | ||
|
|
22e965521a | ||
|
|
b6b1af5959 | ||
|
|
876be3b7b6 | ||
|
|
62f48353ba | ||
|
|
9c65e5a12d | ||
|
|
235e947186 | ||
|
|
0a8bc01870 | ||
|
|
f0bdc01156 | ||
|
|
cd6fed6aae | ||
|
|
7835ebc346 | ||
|
|
3c33581a08 | ||
|
|
3e9a8c9249 | ||
|
|
d6c8f120c6 | ||
|
|
cb2fa1abfc | ||
|
|
25f2cfbff2 | ||
|
|
3404d4cece | ||
|
|
44e1584461 | ||
|
|
8a16188996 | ||
|
|
70518762ae | ||
|
|
a0370912da | ||
|
|
3fee702ee2 | ||
|
|
54d73a6d3b | ||
|
|
9fe9c9d491 | ||
|
|
4300c9ee1b | ||
|
|
a3780bdcea | ||
|
|
6554b68efa | ||
|
|
1a00db9d55 | ||
|
|
fb3ad260da | ||
|
|
ed02b0f260 | ||
|
|
620334439c | ||
|
|
ab5c54b47a | ||
|
|
40e4616520 | ||
|
|
3ebb3bd055 | ||
|
|
25e19b22f7 | ||
|
|
c67266fe1a | ||
|
|
92d747a0f5 | ||
|
|
79b52ed13e | ||
|
|
bd3ad8782a | ||
|
|
6b57619871 | ||
|
|
c8644c80cd | ||
|
|
430ee2280a | ||
|
|
f71c10619c | ||
|
|
b6497c03f6 | ||
|
|
ecb470b938 | ||
|
|
a9d3734117 | ||
|
|
faf77d8212 | ||
|
|
3e6d7af720 | ||
|
|
f69cf8130d | ||
|
|
796208722e | ||
|
|
85dbc54641 | ||
|
|
8381446a69 | ||
|
|
97dc39620c | ||
|
|
7fb973c28d | ||
|
|
4421ac1ceb | ||
|
|
159abfa246 | ||
|
|
4b06700187 | ||
|
|
ee68ca54b8 | ||
|
|
0bfd336796 | ||
|
|
9b967a9af0 | ||
|
|
299aaa5359 | ||
|
|
4e3927bbd0 | ||
|
|
38f19ec778 | ||
|
|
168f4b0303 | ||
|
|
6f8300fb32 | ||
|
|
2ff2923d39 | ||
|
|
aa724e12ba | ||
|
|
e039309a77 | ||
|
|
a4f0a97088 | ||
|
|
9d4c431eef | ||
|
|
1f7179e941 | ||
|
|
4d27bf5492 | ||
|
|
6b85d2cef1 | ||
|
|
aae9930f5e | ||
|
|
eca19aa4bf | ||
|
|
8e02538405 | ||
|
|
928da0cbda | ||
|
|
267693c6ab | ||
|
|
3f99afaa38 | ||
|
|
8e67e5f0fc | ||
|
|
cb1508e6d5 | ||
|
|
070be1ff94 | ||
|
|
7730eacea8 | ||
|
|
c5e3373fe1 | ||
|
|
d7ec620173 | ||
|
|
1c72350d4a | ||
|
|
4ef15f8b81 | ||
|
|
8fc7b614a0 | ||
|
|
0789a69975 | ||
|
|
b405a816c9 | ||
|
|
99c1db3655 | ||
|
|
6b099de63c | ||
|
|
275707fe27 | ||
|
|
c535b96b12 | ||
|
|
6b77cbb0c0 | ||
|
|
a33d8e1061 | ||
|
|
aaf11211dc | ||
|
|
83b2aca942 | ||
|
|
fbae286dff | ||
|
|
ba9b6b54bf | ||
|
|
1677514355 | ||
|
|
ec1042efa3 | ||
|
|
b03ad4ade3 | ||
|
|
04f6e5d2a8 | ||
|
|
16c074db1a | ||
|
|
8d958a2d1d | ||
|
|
74c1cd3be0 | ||
|
|
0fd5f348e2 | ||
|
|
0c9433d0d3 | ||
|
|
8fb34d42dc | ||
|
|
be831eb04a | ||
|
|
43ebab52d4 | ||
|
|
a5f9eaa392 | ||
|
|
cc2064f296 | ||
|
|
f2462cff81 | ||
|
|
b2e25abf08 | ||
|
|
cd0817bd11 | ||
|
|
e88ca8ede1 | ||
|
|
7a83269d98 | ||
|
|
4be1d48215 | ||
|
|
8485ff1e99 | ||
|
|
be66b10f49 | ||
|
|
1f22971493 | ||
|
|
ea943088e5 | ||
|
|
298be4a344 | ||
|
|
f37fc13b7c | ||
|
|
506b74868e | ||
|
|
4a59d1cfda | ||
|
|
7201c8347f | ||
|
|
c8a3df3cfc | ||
|
|
2ef7742903 | ||
|
|
f98135325a | ||
|
|
fd765aa8c8 | ||
|
|
ec93dd5a7d | ||
|
|
0e885ff2d5 | ||
|
|
5893cb575e | ||
|
|
b46136b959 | ||
|
|
390c1e870d | ||
|
|
17b341d83a | ||
|
|
391234ea7b | ||
|
|
92eb3a9ecd | ||
|
|
491445a12f |
21
.github/workflows/build_presets.yml
vendored
@@ -1,6 +1,12 @@
|
||||
name: build
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- dev
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -8,7 +14,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
preset: [MinSizeRel]
|
||||
preset: [Release]
|
||||
runs-on: ${{ matrix.os }}
|
||||
container: devkitpro/devkita64:latest
|
||||
|
||||
@@ -16,16 +22,17 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# fetch latest cmake
|
||||
- uses: lukka/get-cmake@latest
|
||||
- uses: lukka/get-cmake@v3.31.6
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake --preset ${{ matrix.preset }} -DUSE_VFS_GC=0
|
||||
run: cmake --preset ${{ matrix.preset }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build --preset ${{ matrix.preset }} --parallel 4
|
||||
run: cmake --build --preset ${{ matrix.preset }} --parallel $(nproc)
|
||||
|
||||
- uses: actions/upload-artifact@master
|
||||
- name: Deploy
|
||||
if: ${{ github.event_name != 'pull_request' && github.event.action != 'unassigned' }}
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: sphaira-${{ matrix.preset }}
|
||||
path: build/${{ matrix.preset }}/sphaira.nro
|
||||
|
||||
33
.github/workflows/python-usb-export.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: USB Export Python Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: &python_usb_export_paths
|
||||
- 'tools/tests/test_usb_export.py'
|
||||
- 'tools/usb_export.py'
|
||||
- 'tools/usb_common.py'
|
||||
- 'tools/requirements.txt'
|
||||
- '.github/workflows/python-usb-export.yml'
|
||||
pull_request:
|
||||
paths: *python_usb_export_paths
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r tools/requirements.txt
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
python3 tools/tests/test_usb_export.py
|
||||
33
.github/workflows/python-usb-install.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: USB Install Python Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths: &python_usb_install_paths
|
||||
- 'tools/tests/test_usb_install.py'
|
||||
- 'tools/usb_install.py'
|
||||
- 'tools/usb_common.py'
|
||||
- 'tools/requirements.txt'
|
||||
- '.github/workflows/python-usb-install.yml'
|
||||
pull_request:
|
||||
paths: *python_usb_install_paths
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r tools/requirements.txt
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
python3 tools/tests/test_usb_install.py
|
||||
55
.github/workflows/webusb-build.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Build and Deploy WebUSB Site
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'tools/webusb/**'
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install minifiers
|
||||
run: |
|
||||
npm install -g html-minifier-terser terser csso-cli
|
||||
|
||||
- name: Minify HTML
|
||||
run: |
|
||||
html-minifier-terser --collapse-whitespace --remove-comments --minify-css true --minify-js true -o tools/webusb/index.html.min tools/webusb/index.html
|
||||
|
||||
- name: Minify JS
|
||||
run: |
|
||||
terser tools/webusb/index.js -c -m -o tools/webusb/index.js.min
|
||||
|
||||
- name: Minify CSS
|
||||
run: |
|
||||
csso tools/webusb/index.css --output tools/webusb/index.css.min
|
||||
|
||||
- name: Prepare deploy branch
|
||||
run: |
|
||||
rm -rf webusb
|
||||
mkdir webusb
|
||||
cp tools/webusb/index.html.min webusb/index.html
|
||||
cp tools/webusb/index.js.min webusb/index.js
|
||||
cp tools/webusb/index.css.min webusb/index.css
|
||||
cp -r tools/webusb/assets webusb/assets
|
||||
|
||||
- name: Commit and force-push to webusb branch
|
||||
run: |
|
||||
cd webusb
|
||||
git init
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m "Deploy minified webusb build"
|
||||
git branch -M webusb
|
||||
git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git"
|
||||
git push --force origin webusb
|
||||
5
.gitignore
vendored
@@ -25,3 +25,8 @@ compile_commands.json
|
||||
out
|
||||
|
||||
usb_test/
|
||||
__pycache__
|
||||
usb_*.spec
|
||||
|
||||
CMakeUserPresets.json
|
||||
build_patreon.sh
|
||||
|
||||
@@ -21,19 +21,21 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# enable LTO (only in release builds)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||
if (ipo_supported)
|
||||
message(STATUS "IPO / LTO enabled for ALL targets")
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
if (LTO)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||
if (ipo_supported)
|
||||
message(STATUS "IPO / LTO enabled for ALL targets")
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
else()
|
||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||
message(STATUS "IPO / LTO not enabled in debug build")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "IPO / LTO not enabled in debug build")
|
||||
endif()
|
||||
|
||||
function(dkp_fatal_if_not_found var package)
|
||||
|
||||
@@ -16,25 +16,49 @@
|
||||
"name": "Release",
|
||||
"displayName": "Release",
|
||||
"inherits":["core"],
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "MinSizeRel",
|
||||
"LTO": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "RelWithDebInfo",
|
||||
"displayName": "RelWithDebInfo",
|
||||
"name": "Lite",
|
||||
"displayName": "Lite",
|
||||
"inherits":["core"],
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE":"RelWithDebInfo" }
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "MinSizeRel",
|
||||
"LTO": true,
|
||||
|
||||
"ENABLE_NVJPG": false,
|
||||
"ENABLE_NSZ": false,
|
||||
|
||||
"ENABLE_LIBUSBHSFS": false,
|
||||
"ENABLE_LIBUSBDVD": false,
|
||||
"ENABLE_FTPSRV": false,
|
||||
"ENABLE_LIBHAZE": false,
|
||||
|
||||
"ENABLE_AUDIO_MP3": false,
|
||||
"ENABLE_AUDIO_OGG": false,
|
||||
"ENABLE_AUDIO_WAV": false,
|
||||
"ENABLE_AUDIO_FLAC": false,
|
||||
|
||||
"ENABLE_DEVOPTAB_HTTP": false,
|
||||
"ENABLE_DEVOPTAB_NFS": false,
|
||||
"ENABLE_DEVOPTAB_SMB2": false,
|
||||
"ENABLE_DEVOPTAB_FTP": false,
|
||||
"ENABLE_DEVOPTAB_SFTP": false,
|
||||
"ENABLE_DEVOPTAB_WEBDAV": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MinSizeRel",
|
||||
"displayName": "MinSizeRel",
|
||||
"name": "Dev",
|
||||
"displayName": "Dev",
|
||||
"inherits":["core"],
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE":"MinSizeRel" }
|
||||
},
|
||||
{
|
||||
"name": "Debug",
|
||||
"displayName": "Debug",
|
||||
"inherits":["core"],
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE":"Debug" }
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "MinSizeRel",
|
||||
"LTO": false,
|
||||
"DEV_BUILD": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
@@ -44,18 +68,13 @@
|
||||
"jobs": 16
|
||||
},
|
||||
{
|
||||
"name": "RelWithDebInfo",
|
||||
"configurePreset": "RelWithDebInfo",
|
||||
"name": "Lite",
|
||||
"configurePreset": "Lite",
|
||||
"jobs": 16
|
||||
},
|
||||
{
|
||||
"name": "MinSizeRel",
|
||||
"configurePreset": "MinSizeRel",
|
||||
"jobs": 16
|
||||
},
|
||||
{
|
||||
"name": "Debug",
|
||||
"configurePreset": "Debug",
|
||||
"name": "Dev",
|
||||
"configurePreset": "Dev",
|
||||
"jobs": 16
|
||||
}
|
||||
]
|
||||
|
||||
40
README.md
@@ -4,16 +4,16 @@ A homebrew menu for the Nintendo Switch.
|
||||
|
||||
[See the GBATemp thread for more details / discussion](https://gbatemp.net/threads/sphaira-hbmenu-replacement.664523/).
|
||||
|
||||
[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.
|
||||
[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
|
||||
|
||||
| | |
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
 | 
|
||||
|
||||
## Bug reports
|
||||
|
||||
@@ -86,16 +86,22 @@ The output will be found in `build/MinSizeRel/sphaira.nro`
|
||||
|
||||
## Credits
|
||||
|
||||
- borealis
|
||||
- stb
|
||||
- yyjson
|
||||
- nx-hbmenu
|
||||
- nx-hbloader
|
||||
- deko3d-nanovg
|
||||
- libpulsar
|
||||
- minIni
|
||||
- GBATemp
|
||||
- hb-appstore
|
||||
- haze
|
||||
- nxdumptool (for gamecard bin dumping and rsa verify code)
|
||||
- [borealis](https://github.com/natinusala/borealis)
|
||||
- [stb](https://github.com/nothings/stb)
|
||||
- [yyjson](https://github.com/ibireme/yyjson)
|
||||
- [nx-hbmenu](https://github.com/switchbrew/nx-hbmenu)
|
||||
- [nx-hbloader](https://github.com/switchbrew/nx-hbloader)
|
||||
- [deko3d-nanovg](https://github.com/Adubbz/nanovg-deko3d)
|
||||
- [libpulsar](https://github.com/p-sam/switch-libpulsar)
|
||||
- [minIni](https://github.com/compuphase/minIni)
|
||||
- [GBATemp](https://gbatemp.net/threads/sphaira-hbmenu-replacement.664523/)
|
||||
- [hb-appstore](https://github.com/fortheusers/hb-appstore)
|
||||
- [haze](https://github.com/Atmosphere-NX/Atmosphere/tree/master/troposphere/haze)
|
||||
- [nxdumptool](https://github.com/DarkMatterCore/nxdumptool) (for gamecard bin dumping and rsa verify code)
|
||||
- [Liam0](https://github.com/ThatNerdyPikachu/switch-010editor-templates) (for ticket / cert structs)
|
||||
- [libusbhsfs](https://github.com/DarkMatterCore/libusbhsfs)
|
||||
- [libnxtc](https://github.com/DarkMatterCore/libnxtc)
|
||||
- [oss-nvjpg](https://github.com/averne/oss-nvjpg)
|
||||
- [nsz](https://github.com/nicoboss/nsz)
|
||||
- [themezer](https://themezer.net/)
|
||||
- Everyone who has contributed to this project!
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"url": "https://github.com/ITotalJustice/ftpsrv",
|
||||
"assets": [
|
||||
{
|
||||
"name": "switch"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"url": "https://github.com/ITotalJustice/untitled"
|
||||
}
|
||||
@@ -1,23 +1,24 @@
|
||||
{
|
||||
"[Applet Mode]": " | Applet Modus |",
|
||||
"[Applet Mode]": "[Applet Modus]",
|
||||
"No Internet": "Kein Internet",
|
||||
"Switch-Handheld!": "Handheld!",
|
||||
"Switch-Docked!": "Angedockt!",
|
||||
"Switch-Handheld!": "Handheld-Modus!",
|
||||
"Switch-Docked!": "TV-Modus!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "Warnung! Protokollierung ist aktiviert, Sphaira läuft langsam!",
|
||||
"Audio disabled due to suspended game": "Audio deaktivert wegen Spielabbruch",
|
||||
"Are you sure you wish to cancel?": "Bist du sicher dass du abbrechen willst?",
|
||||
"An error occurred": "",
|
||||
"An error occurred": "Ein Fehler ist aufgetreten.",
|
||||
"If this message appears repeatedly, please open an issue.": "Bei wiederholtem Auftreten bitte Issue erstellen.",
|
||||
|
||||
"Menu Options": " Menü | Optionen",
|
||||
"Menu Options": "Menü | Optionen",
|
||||
"Menu": "Menü",
|
||||
"Theme": "Themes",
|
||||
"Theme Options": " Themes | Optionen",
|
||||
"Select Theme": "Theme wählen",
|
||||
"Theme": "Designs",
|
||||
"Theme Options": "Designs | Optionen",
|
||||
"Select Theme": "Design wählen",
|
||||
"Music": "Musik",
|
||||
"12 Hour Time": "12-Std Zeitformat",
|
||||
"Download Default Music": "",
|
||||
"Failed to download default_music.bfstm, please try again": "",
|
||||
"Overwrite current default music?": "",
|
||||
"Download Default Music": "Standardmusik herunterladen",
|
||||
"Failed to download default_music.bfstm, please try again": "Fehler beim Herunterladen von default_music.bfstm. Bitte versuchen Sie es erneut.",
|
||||
"Overwrite current default music?": "Aktuelle Standardmusik überschreiben?",
|
||||
|
||||
"Network": "Konnektivität",
|
||||
"Network Options": "Konnektivität | Optionen",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "NXLink | Verbunden",
|
||||
"Nxlink Upload": "NXLink | wird hochgeladen...",
|
||||
"Nxlink Finished": "NXLink | Hochladen beendet",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "HDD Schreibgeschützt",
|
||||
|
||||
"Language": "Sprache",
|
||||
"Auto": "Systemsprache",
|
||||
@@ -46,33 +49,46 @@
|
||||
"Ukrainian": "Українська",
|
||||
|
||||
"Misc": "Extras",
|
||||
"Misc Options": " Extras | Optionen",
|
||||
"Misc Options": "Extras | Optionen",
|
||||
"Games": "Spiele",
|
||||
"Game Options": "",
|
||||
"Hide forwarders": "",
|
||||
"Launch random game": "",
|
||||
"Game Options": "Spiel | Optionen",
|
||||
"Hide forwarders": "Forwarders ausblenden",
|
||||
"Launch random game": "Zufälliges Spiel starten",
|
||||
"List meta records": "",
|
||||
"Entries": "",
|
||||
"Failed to list application meta entries": "",
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Dump options": "",
|
||||
"Dump Options": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Created nested folder": "",
|
||||
"Append folder with .xci": "",
|
||||
"Trim XCI": "",
|
||||
"Label trimmed XCI": "",
|
||||
"Multi-threaded USB transfer": "",
|
||||
"Dump All Bins": "",
|
||||
"Dump XCI": "",
|
||||
"Dump Card ID Set": "",
|
||||
"Dump Card UID": "",
|
||||
"Dump Certificate": "",
|
||||
"Dump Initial Data": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"microSD card (/dumps/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Success": "",
|
||||
|
||||
"Themezer": "Themezer | NX Themes",
|
||||
"Themezer Options": " Themezer | Optionen",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Lade JSON-File",
|
||||
"Select asset to download for ": "Wähle Asset für den Download von ",
|
||||
"Failed to download json": "Download von JSON-Datei fehlgeschlagen",
|
||||
"Failed to download app!": "Download der App fehlgeschlagen",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
@@ -98,19 +115,19 @@
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"Failed to install, press B to exit...": "",
|
||||
"Install success!": "",
|
||||
"Install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
"Connected, starting transfer...": "",
|
||||
"Connected, waiting for file list...": "Verbunden, warte auf die Dateiliste...",
|
||||
"Connected, starting transfer...": "Verbunden, Übertragung startet...",
|
||||
"Failed to init usb, press B to exit...": "",
|
||||
"Waiting for connection...": "",
|
||||
"Transferring data...": "",
|
||||
"Waiting for connection...": "Warten auf Verbindung...",
|
||||
"Transferring data...": "Datenübertragung...",
|
||||
"USB connected, sending file list": "",
|
||||
"Sent file list, waiting for command...": "",
|
||||
"waiting for usb connection...": "",
|
||||
"waiting for usb connection...": "Warten auf USB Verbindung...",
|
||||
"Disable MTP for usb install": "",
|
||||
"Re-enabled MTP": "",
|
||||
"Installed via usb": "",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "",
|
||||
"No GameCard inserted": "Keine Spielkarte eingelegt",
|
||||
"GameCard is already trimmed!": "",
|
||||
"WARNING: GameCard is already trimmed!": "",
|
||||
"Continue": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
|
||||
@@ -160,15 +180,15 @@
|
||||
"Negative image": "Negativ-Bild",
|
||||
"Format": "Format",
|
||||
"Trimming Format": "Beschnitt-Format",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Externes Lichtfilter",
|
||||
"Load Default": "Standard laden",
|
||||
|
||||
"Advanced": "Erweitert...",
|
||||
"Web": "",
|
||||
"Select URL": "",
|
||||
"Enter custom URL": "",
|
||||
"Enter URL": "",
|
||||
|
||||
"Advanced": "Erweitert",
|
||||
"Advanced Options": " Erweitert | Optionen",
|
||||
"Logging": "Protokollieren",
|
||||
"Replace hbmenu on exit": "hbmenu durch sphaira ersetzen",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "hbmenu wurde wiederhergestellt",
|
||||
"Restart Sphaira?": "sphaira erneut starten?",
|
||||
"Press OK to restart Sphaira": "Drücke OK um sphaira erneut zustarten",
|
||||
"Boost CPU during transfer": "",
|
||||
"Text scroll speed": "Laufschrift Tempo",
|
||||
"Slow": "Niedrig",
|
||||
"Normal": "Mittel",
|
||||
"Fast": "Hoch",
|
||||
"Set left-side menu": "",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "Einhängepunkt",
|
||||
"System memory": "NAND Systemspeicher",
|
||||
"microSD card": "SD-Karte",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
@@ -222,15 +243,17 @@
|
||||
"Creating Control": "Erstelle Control",
|
||||
"Creating Meta": "Erstelle Meta",
|
||||
"Writing Nca": "Schreibe NCA",
|
||||
"Updating ncm databse": "Aktualisiere NCM-Datenbank",
|
||||
"Updating ncm database": "Aktualisiere NCM-Datenbank",
|
||||
"Pushing application record": "Übertrage Anwendungsdaten",
|
||||
"Failed to install forwarder": "Fehler beim installieren des Forwarders",
|
||||
"Unstar": "Kein Favorit",
|
||||
"Star": "Favorit",
|
||||
"Unstarred ": "Favorit entfernt ",
|
||||
"Starred ": "Favorit ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
|
||||
"AppStore": "hb-AppStore",
|
||||
"Appstore": "",
|
||||
"Appstore": "hb-AppStore",
|
||||
"Store": "hb-Store",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Rubrik: %s | Sort.nach.: %s | Ordnung: %s",
|
||||
"AppStore Options": " hb-AppStore | Optionen",
|
||||
@@ -238,7 +261,7 @@
|
||||
"Changelog": "Neuerungen",
|
||||
"Details": "Details",
|
||||
"version: %s": "Version: %s",
|
||||
"updated: %s": "Letztes Update am: %s",
|
||||
"updated: %s": "Letztes Update: %s",
|
||||
"category: %s": "Rubrik: %s",
|
||||
"extracted: %.2f MiB": "Größe: %.2f MiB",
|
||||
"app_dls: %s": "Anzahl Downloads: %s",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Kopieren",
|
||||
"Copying ": "Kopiert wird: ",
|
||||
"Paste": "Einfügen",
|
||||
"Paste ": "Einfügen von: ",
|
||||
" file(s)?": " Datei/en?",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "Eingefügt wird: ",
|
||||
"Pasting": "Eingefügt wurde:",
|
||||
"Rename": "Umbenennen",
|
||||
"Set New File Name": "Neuen Dateinamen festlegen",
|
||||
"Failed to delete directory": "",
|
||||
"Failed to delete file": "",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Neuer Ordner",
|
||||
"Set Folder Name": "Ordner umbenennen",
|
||||
"Creating ": "Erstellt wird: ",
|
||||
"View as text (unfinished)": "Als Text anzeigen",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Als Text anzeigen",
|
||||
"Hash": "",
|
||||
"Hash Options": "",
|
||||
"Hashing": "",
|
||||
"Failed to hash file...": "",
|
||||
"Ignore read only": "Schreibschutz umgehen?",
|
||||
"Mount": "Einhängen",
|
||||
"Sd": "SD-Karte | Root-Verzeichnis",
|
||||
@@ -302,9 +330,10 @@
|
||||
"Launch ": "Starte ",
|
||||
"Launch option for: ": "Start Option für: ",
|
||||
"Select launcher for: ": "Wähle Launcher für: ",
|
||||
"Close FileBrowser?": "Datei-Manager schließen?",
|
||||
|
||||
"Sort By": "Sortierung",
|
||||
"Sort Options": " Sortierung | Optionen",
|
||||
"Sort Options": "Sortierung | Optionen",
|
||||
"Filter": "Rubrik",
|
||||
"All": "Alles anzeigen",
|
||||
"Emulators": "Emulatoren",
|
||||
@@ -335,25 +364,25 @@
|
||||
"Search": "Suchen",
|
||||
|
||||
"Options": "Optionen",
|
||||
"Split": "",
|
||||
"OK": "OK",
|
||||
"Back": "Zurück",
|
||||
"Select": "Auswählen",
|
||||
"Open": "Öffne",
|
||||
"Close": "Schließe",
|
||||
"Launch": "Starte",
|
||||
"Restart": "Neustart",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "Kein Favorit",
|
||||
"Star": "Favorit",
|
||||
"Yes": "Ja",
|
||||
"No": "Nein",
|
||||
"On": "",
|
||||
"Off": "",
|
||||
"On": "An",
|
||||
"Off": "Aus",
|
||||
|
||||
"Install": "Installieren",
|
||||
"Install Selected files?": "",
|
||||
"Installing ": "Installiert wird: ",
|
||||
"Installed ": "",
|
||||
"Installed ": "Installiert ",
|
||||
"Installed!": "Installiert!",
|
||||
"Trying to load ": "Versucht zu laden wird: ",
|
||||
"Checking MD5": "Checke MD5 Prüfsumme",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Entfernen",
|
||||
"Completely remove ": "Komplett gelöscht wird: ",
|
||||
"Removing ": "Entfernt wird: ",
|
||||
"Removed ": "Entfernt wurde: ",
|
||||
"Uninstalling ": "Deinstalliert wird: ",
|
||||
"Removed ": "Entfernt wurde: ",
|
||||
|
||||
"Download": "Download",
|
||||
"Downloading ": "Heruntergeladen wird: ",
|
||||
"Downloaded ": "Heruntergeladen wurde: ",
|
||||
"Download via the Network options!": "",
|
||||
|
||||
"Update": "Update",
|
||||
"Update avaliable: ": "Update verfügbar: ",
|
||||
@@ -380,13 +410,13 @@
|
||||
"Updated to ": "Aktualisiert auf: ",
|
||||
"Failed to download update": "Herunterladen des Updates fehlgeschlagen!",
|
||||
|
||||
"%zu hours %zu minutes remaining": "",
|
||||
"%zu minutes %zu seconds remaining": "",
|
||||
"%zu seconds remaining": "",
|
||||
"%zu hours %zu minutes remaining": "%zu Stunden und %zu Minuten verbleibend",
|
||||
"%zu minutes %zu seconds remaining": "%zu Minuten und %zu Sekunde verbleibend",
|
||||
"%zu seconds remaining": "%zu Sekunde verbleibend",
|
||||
|
||||
"Loading...": "Wird geladen...",
|
||||
"Loading": "Wird geladen",
|
||||
"Empty!": "Keine Daten!",
|
||||
"Not Ready...": "Nicht bereit...",
|
||||
"Error loading page!": "Ladefehler!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "No Internet",
|
||||
"Switch-Handheld!": "Switch-Handheld!",
|
||||
"Switch-Docked!": "Switch-Docked!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "Warning! Logs are enabled, Sphaira will run slowly!",
|
||||
"Audio disabled due to suspended game": "Audio disabled due to suspended game",
|
||||
"Are you sure you wish to cancel?": "Are you sure you wish to cancel?",
|
||||
"An error occurred": "An error occurred",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink Connected",
|
||||
"Nxlink Upload": "Nxlink Upload",
|
||||
"Nxlink Finished": "Nxlink Finished",
|
||||
"Hdd": "Hdd",
|
||||
"Hdd write protect": "Hdd write protect",
|
||||
|
||||
"Language": "Language",
|
||||
"Auto": "Auto",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "No meta entries found...\n",
|
||||
"Updating application record list": "Updating application record list",
|
||||
"Dump": "Dump",
|
||||
"Dump options": "Dump options",
|
||||
"Dump Options": "Dump Options",
|
||||
"Select content to dump": "Select content to dump",
|
||||
"Dump All": "Dump All",
|
||||
"Dump Application": "Dump Application",
|
||||
"Dump Patch": "Dump Patch",
|
||||
"Dump AddOnContent": "Dump AddOnContent",
|
||||
"Dump DataPatch": "Dump DataPatch",
|
||||
"Created nested folder": "Created nested folder",
|
||||
"Append folder with .xci": "Append folder with .xci",
|
||||
"Trim XCI": "Trim XCI",
|
||||
"Label trimmed XCI": "Label trimmed XCI",
|
||||
"Multi-threaded USB transfer": "Multi-threaded USB transfer",
|
||||
"Dump All Bins": "Dump All Bins",
|
||||
"Dump XCI": "Dump XCI",
|
||||
"Dump Card ID Set": "Dump Card ID Set",
|
||||
"Dump Card UID": "Dump Card UID",
|
||||
"Dump Certificate": "Dump Certificate",
|
||||
"Dump Initial Data": "Dump Initial Data",
|
||||
"Select dump location": "Select dump location",
|
||||
"microSD card (/dumps/NSP/)": "microSD card (/dumps/NSP/)",
|
||||
"microSD card (/dumps/)": "microSD card (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "USB transfer (Switch 2 Switch)",
|
||||
"/dev/null (Speed Test)": "/dev/null (Speed Test)",
|
||||
"Dumping": "Dumping",
|
||||
"Dump successfull!": "Dump successfull!",
|
||||
"Dump failed!": "Dump failed!",
|
||||
"Success": "Success",
|
||||
"Delete successfull!": "Delete successfull!",
|
||||
"Delete failed!": "Delete failed!",
|
||||
"Success": "Success",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer Options",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Downloading json",
|
||||
"Select asset to download for ": "Select asset to download for ",
|
||||
"Failed to download json": "Failed to download json",
|
||||
"Failed to download app!": "Failed to download app!",
|
||||
|
||||
"FTP Install": "FTP Install",
|
||||
"FTP Install (EXPERIMENTAL)": "FTP Install (EXPERIMENTAL)",
|
||||
"Connection Type: WiFi | Strength: ": "Connection Type: WiFi | Strength: ",
|
||||
"Connection Type: Ethernet": "Connection Type: Ethernet",
|
||||
"Connection Type: None": "Connection Type: None",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "Password:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "Passphrase",
|
||||
"Failed to install via FTP, press B to exit...": "Failed to install via FTP, press to exit...",
|
||||
"Ftp install success!": "Ftp install success!",
|
||||
"Ftp install failed!": "Ftp install failed!",
|
||||
"Failed to install, press B to exit...": "Failed to install, press to exit...",
|
||||
"Install success!": "Install success!",
|
||||
"Install failed!": "Install failed!",
|
||||
"USB Install": "USB Install",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "Connected, waiting for file list...",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "GC",
|
||||
"System memory %.1f GB": "System memory %.1f GB",
|
||||
"microSD card %.1f GB": "microSD card %.1f GB",
|
||||
"Nand Install": "Nand Install",
|
||||
"SD Card Install": "SD Card Install",
|
||||
"Exit": "Exit",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Install disabled...\nPlease enable installing via the install options.",
|
||||
"No GameCard inserted": "No GameCard inserted",
|
||||
"GameCard is already trimmed!": "GameCard is already trimmed!",
|
||||
"WARNING: GameCard is already trimmed!": "WARNING: GameCard is already trimmed!",
|
||||
"Continue": "Continue",
|
||||
"Gc install success!": "Gc install success!",
|
||||
"Gc install failed!": "Gc install failed!",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "Negative image",
|
||||
"Format": "Format",
|
||||
"Trimming Format": "Trimming Format",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "External Light Filter",
|
||||
"Load Default": "Load Default",
|
||||
|
||||
"Web": "Web",
|
||||
"Select URL": "Select URL",
|
||||
"Enter custom URL": "Enter custom URL",
|
||||
"Enter URL": "Enter URL",
|
||||
|
||||
"Advanced": "Advanced",
|
||||
"Advanced Options": "Advanced Options",
|
||||
"Logging": "Logging",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "Restored hbmenu",
|
||||
"Restart Sphaira?": "Restart Sphaira?",
|
||||
"Press OK to restart Sphaira": "Press OK to restart Sphaira",
|
||||
"Boost CPU during transfer": "Boost CPU during transfer",
|
||||
"Text scroll speed": "Text scroll speed",
|
||||
"Slow": "Slow",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Fast",
|
||||
"Set left-side menu": "Set left-side menu",
|
||||
"Set right-side menu": "Set right-side menu",
|
||||
"Install options": "Install options",
|
||||
"Install Options": "Install Options",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "Install location",
|
||||
"System memory": "System memory",
|
||||
"microSD card": "microSD card",
|
||||
"Boost CPU clock": "Boost CPU clock",
|
||||
"Allow downgrade": "Allow downgrade",
|
||||
"Skip if already installed": "Skip if already installed",
|
||||
"Ticket only": "Ticket only",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "Creating Control",
|
||||
"Creating Meta": "Creating Meta",
|
||||
"Writing Nca": "Writing Nca",
|
||||
"Updating ncm databse": "Updating ncm databse",
|
||||
"Updating ncm database": "Updating ncm database",
|
||||
"Pushing application record": "Pushing application record",
|
||||
"Failed to install forwarder": "Failed to install forwarder",
|
||||
"Unstar": "Unstar",
|
||||
"Star": "Star",
|
||||
"Unstarred ": "Unstarred ",
|
||||
"Starred ": "Starred ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Failed to remove old forwarder, please manually remove it!",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Copy",
|
||||
"Copying ": "Copying ",
|
||||
"Paste": "Paste",
|
||||
"Paste ": "Paste ",
|
||||
" file(s)?": " file(s)?",
|
||||
"Paste file(s)?": "Paste file(s)?",
|
||||
"Pasting ": "Pasting ",
|
||||
"Pasting": "Pasting",
|
||||
"Rename": "Rename",
|
||||
"Set New File Name": "Set New File Name",
|
||||
"Failed to delete directory": "Failed to delete directory",
|
||||
"Failed to delete file": "Failed to delete file",
|
||||
"Extract zip": "Extract zip",
|
||||
"Extract Options": "Extract Options",
|
||||
"Extract here": "Extract here",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Create Folder",
|
||||
"Set Folder Name": "Set Folder Name",
|
||||
"Creating ": "Creating ",
|
||||
"View as text (unfinished)": "View as text (unfinished)",
|
||||
"Upload": "Upload",
|
||||
"Select upload location": "Select upload location",
|
||||
"No upload locations set!": "No upload locations set!",
|
||||
"Uploading": "Uploading",
|
||||
"Upload successfull!": "Upload successfull!",
|
||||
"Upload failed!": "Upload failed!",
|
||||
"View as text (unfinished)": "View as text (unfinished)",
|
||||
"Hash": "Hash",
|
||||
"Hash Options": "Hash Options",
|
||||
"Hashing": "Hashing",
|
||||
"Failed to hash file...": "Failed to hash file...",
|
||||
"Ignore read only": "Ignore read only",
|
||||
"Mount": "Mount",
|
||||
"Sd": "Sd",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "Launch ",
|
||||
"Launch option for: ": "Launch option for: ",
|
||||
"Select launcher for: ": "Select launcher for: ",
|
||||
"Close FileBrowser?": "Close FileBrowser?",
|
||||
|
||||
"Sort By": "Sort By",
|
||||
"Sort Options": "Sort Options",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "Search",
|
||||
|
||||
"Options": "Options",
|
||||
"Split": "Split",
|
||||
"OK": "OK",
|
||||
"Back": "Back",
|
||||
"Select": "Select",
|
||||
"Open": "Open",
|
||||
"Close": "Close",
|
||||
"Launch": "Launch",
|
||||
"Restart": "Restart",
|
||||
"Next": "Next",
|
||||
"Prev": "Prev",
|
||||
"Unstar": "Unstar",
|
||||
"Star": "Star",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"On": "On",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Remove",
|
||||
"Completely remove ": "Completely remove ",
|
||||
"Removing ": "Removing ",
|
||||
"Removed ": "Removed ",
|
||||
"Uninstalling ": "Uninstalling ",
|
||||
"Removed ": "Removed ",
|
||||
|
||||
"Download": "Download",
|
||||
"Downloading ": "Downloading ",
|
||||
"Downloaded ": "Downloaded ",
|
||||
"Download via the Network options!": "Download via the Network options!",
|
||||
|
||||
"Update": "Update",
|
||||
"Update avaliable: ": "Update avaliable: ",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "Empty!",
|
||||
"Not Ready...": "Not Ready...",
|
||||
"Error loading page!": "Error loading page!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
"No Internet": "Sin Internet",
|
||||
"Switch-Handheld!": "¡Switch-Modo-Portátil!",
|
||||
"Switch-Docked!": "¡Switch-Modo-TV!",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"Are you sure you wish to cancel?": "¿Estás seguro que deseas cancelar?",
|
||||
"An error occurred": "",
|
||||
"If this message appears repeatedly, please open an issue.": "Si este mensaje aparece repetidamente, por favor abrir un 'issue'.",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "ADVERTENCIA: ¡el registro de eventos está activado, Sphaira correrá más lento!",
|
||||
"Audio disabled due to suspended game": "Audio deshabilitado debido al juego suspendido",
|
||||
"Are you sure you wish to cancel?": "¿Realmente deseas cancelar?",
|
||||
"An error occurred": "Ha ocurrido un error",
|
||||
"If this message appears repeatedly, please open an issue.": "Si este mensaje aparece repetidamente, por favor abra un 'issue'.",
|
||||
|
||||
"Menu Options": "Opciones de menú",
|
||||
"Menu": "Menú",
|
||||
@@ -14,10 +15,10 @@
|
||||
"Theme Options": "Opciones de tema",
|
||||
"Select Theme": "Seleccionar tema",
|
||||
"Music": "Música",
|
||||
"12 Hour Time": "",
|
||||
"Download Default Music": "",
|
||||
"Failed to download default_music.bfstm, please try again": "",
|
||||
"Overwrite current default music?": "",
|
||||
"12 Hour Time": "Reloj de 12 horas",
|
||||
"Download Default Music": "Descargar música por defecto",
|
||||
"Failed to download default_music.bfstm, please try again": "Falló la descarga de default_music.bfstm. Inténtelo de nuevo.",
|
||||
"Overwrite current default music?": "¿Sobreescribir la música por defecto actual?",
|
||||
|
||||
"Network": "Red",
|
||||
"Network Options": "Opciones de red",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "NXlink conectado",
|
||||
"Nxlink Upload": "NXlink subida",
|
||||
"Nxlink Finished": "NXlink finalizado",
|
||||
"Hdd": "Disco Duro",
|
||||
"Hdd write protect": "Disco duro de solo lectura",
|
||||
|
||||
"Language": "Idioma",
|
||||
"Auto": "Automático",
|
||||
@@ -45,104 +48,121 @@
|
||||
"Vietnamese": "Vietnamese",
|
||||
"Ukrainian": "Українська",
|
||||
|
||||
"Misc": "Varios",
|
||||
"Misc Options": "Opciones varias",
|
||||
"Misc": "Misceláneas",
|
||||
"Misc Options": "Opciones misceláneas",
|
||||
"Games": "Juegos",
|
||||
"Game Options": "",
|
||||
"Hide forwarders": "",
|
||||
"Launch random game": "",
|
||||
"List meta records": "",
|
||||
"Entries": "",
|
||||
"Failed to list application meta entries": "",
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Game Options": "Opciones de juegos",
|
||||
"Hide forwarders": "Ocultar forwarders",
|
||||
"Launch random game": "Ejecutar un juego aleatorio",
|
||||
"List meta records": "Listar registros de metadatos",
|
||||
"Entries": "Entradas",
|
||||
"Failed to list application meta entries": "Error al listar las entradas de metadatos de las aplicaciones",
|
||||
"No meta entries found...\n": "No se encontraron entradas de metadatos…\n",
|
||||
"Updating application record list": "Actualizando la lista de registros de aplicaciones",
|
||||
"Dump": "Volcar",
|
||||
"Dump options": "Opciones de volcado",
|
||||
"Dump Options": "Opciones de volcado",
|
||||
"Select content to dump": "Seleccione el contenido a volcar",
|
||||
"Dump All": "Volcar todo",
|
||||
"Dump Application": "Volcar aplicación",
|
||||
"Dump Patch": "Volcar parche",
|
||||
"Dump AddOnContent": "Volcar contenido adicional",
|
||||
"Dump DataPatch": "Volcar datos de parche",
|
||||
"Created nested folder": "Se creó una carpeta anidadada",
|
||||
"Append folder with .xci": "Adjuntar carpeta con archivo .XCI",
|
||||
"Trim XCI": "Archivo XCI recortado",
|
||||
"Label trimmed XCI": "Archivo XCI con etiqueta removida",
|
||||
"Multi-threaded USB transfer": "Transferencia USB multi-hilos",
|
||||
"Dump All Bins": "Volcar todos los binarios",
|
||||
"Dump XCI": "Volcar archivo XCI",
|
||||
"Dump Card ID Set": "Volcar conjunto de ID de tarjeta",
|
||||
"Dump Card UID": "Volcar UID de tarjeta",
|
||||
"Dump Certificate": "Volcar Certificado",
|
||||
"Dump Initial Data": "Volcar datos iniciales",
|
||||
"Select dump location": "Escoja la ubicación para el volcado",
|
||||
"microSD card (/dumps/)": "Tarjeta microSD (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "Transferencia USB (de Switch a Switch)",
|
||||
"/dev/null (Speed Test)": "Medir la velocidad usando dispositivo /dev/null",
|
||||
"Dumping": "Volcando",
|
||||
"Dump successfull!": "¡Volcado exitoso!",
|
||||
"Dump failed!": "¡Error en el volcado!",
|
||||
"Delete successfull!": "¡Borrado exitoso!",
|
||||
"Delete failed!": "¡Error al borrar!",
|
||||
"Success": "Proceso exitoso",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Opciones de Themezer",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Página",
|
||||
"Page %zu / %zu": "Pág. %zu / %zu",
|
||||
"Enter Page Number": "Ingresar Número de Página",
|
||||
"Bad Page": "Página Errónea",
|
||||
"Download theme?": "¿Descargar Tema?",
|
||||
"Enter Page Number": "Ingrese número de página",
|
||||
"Bad Page": "Página errónea",
|
||||
"Download theme?": "¿Descargar tema?",
|
||||
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Descargando json",
|
||||
"Downloading json": "Descargando JSON",
|
||||
"Select asset to download for ": "Seleccionar recurso a descargar para ",
|
||||
"Failed to download json": "Error al descargar JSON",
|
||||
"Failed to download app!": "¡Error al descargar aplicación!",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
"Host:": "",
|
||||
"Port:": "",
|
||||
"Username:": "",
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
"Connected, starting transfer...": "",
|
||||
"Failed to init usb, press B to exit...": "",
|
||||
"Waiting for connection...": "",
|
||||
"Transferring data...": "",
|
||||
"USB connected, sending file list": "",
|
||||
"Sent file list, waiting for command...": "",
|
||||
"waiting for usb connection...": "",
|
||||
"Disable MTP for usb install": "",
|
||||
"Re-enabled MTP": "",
|
||||
"Installed via usb": "",
|
||||
"Usb install success!": "",
|
||||
"Usb install failed!": "",
|
||||
"Press B to exit...": "",
|
||||
"GameCard Install": "",
|
||||
"GameCard": "",
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
"FTP Install": "Instalación vía FTP",
|
||||
"Connection Type: WiFi | Strength: ": "Tipo de conexión: WiFi | Fuerza de la señal",
|
||||
"Connection Type: Ethernet": "Tipo de conexión: Ethernet",
|
||||
"Connection Type: None": "Tipo de conexión: ninguna",
|
||||
"Host:": "Host",
|
||||
"Port:": "Puerto",
|
||||
"Username:": "Nombre de usuario",
|
||||
"Password:": "Contraseña",
|
||||
"SSID:": "SSID",
|
||||
"Passphrase:": "Parafrase",
|
||||
"Failed to install, press B to exit...": "Error al instalar vía FTP. Presione B para salir…",
|
||||
"Install success!": "¡Instalación vía FTP satisfactoria!",
|
||||
"Install failed!": "¡Error en la instalación vía FTP!",
|
||||
"USB Install": "Instalación vía USB",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "Conectado, esperando lista de archivos…",
|
||||
"Connected, starting transfer...": "Conectado, iniciando la transferencia…",
|
||||
"Failed to init usb, press B to exit...": "Error al iniciar el USB. Presione B para salir…",
|
||||
"Waiting for connection...": "Esperando conexión…",
|
||||
"Transferring data...": "Tranfiriendo datos…",
|
||||
"USB connected, sending file list": "USB conectado, enviando listado de archivos",
|
||||
"Sent file list, waiting for command...": "Lista de archivos enviada, esperando comando…",
|
||||
"waiting for usb connection...": "Esperando por conexión USB…",
|
||||
"Disable MTP for usb install": "Instalación vía MTP deshabilitada",
|
||||
"Re-enabled MTP": "Instalación vía MTP rehabilitada",
|
||||
"Installed via usb": "Instalado vía USB",
|
||||
"Usb install success!": "Instalación USB satisfactoria",
|
||||
"Usb install failed!": "Error en instalación USB",
|
||||
"Press B to exit...": "Presione B para salir…",
|
||||
"GameCard Install": "Instalación de tarjeta de juego",
|
||||
"GameCard": "Tarjeta de juego",
|
||||
"GC": "TJ",
|
||||
"System memory %.1f GB": "Memoria del sistema %.1f GB",
|
||||
"microSD card %.1f GB": "Tarjeta microSD %.1f GB",
|
||||
"Exit": "Salir",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Instalación deshabilitada…\nPor favor habilite instalar aplicaciones en las opciones de instalación",
|
||||
"No GameCard inserted": "No hay una tarjeta de juego insertada",
|
||||
"GameCard is already trimmed!": "¡La tarjeta de juego ya se encuentra recortada!",
|
||||
"WARNING: GameCard is already trimmed!": "ADVERTENCIA: ¡la tarjeta de juego ya se encuentra recortada!",
|
||||
"Continue": "Continuar",
|
||||
"Gc install success!": "Instalación de TJ satisfactoria",
|
||||
"Gc install failed!": "Fallo al instalar TJ",
|
||||
|
||||
"IRS (Infrared Joycon Camera)": "",
|
||||
"IRS": "",
|
||||
"Irs": "IRS",
|
||||
"Ambient Noise Level: ": "Nivel de Ruido Ambiente",
|
||||
"IRS (Infrared Joycon Camera)": "IRS (Cámara infraroja del JoyCon)",
|
||||
"IRS": "IRS",
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "Nivel de ruido ambiente",
|
||||
"Controller": "Control",
|
||||
"Pad ": "GamePad ",
|
||||
"Pad ": "Pad ",
|
||||
"HandHeld": "Portátil",
|
||||
" (Available)": " (Disponible)",
|
||||
" (Unsupported)": "(No Compatible)",
|
||||
" (Unsupported)": "(No compatible)",
|
||||
" (Unconnected)": " (Desconectado)",
|
||||
"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",
|
||||
@@ -160,90 +180,93 @@
|
||||
"Negative image": "Imagen negativa",
|
||||
"Format": "Formato",
|
||||
"Trimming Format": "Formato de recorte",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Filtro de luz externa",
|
||||
"Load Default": "Cargar predeterminado",
|
||||
|
||||
"Web": "Web",
|
||||
"Select URL": "Seleccione la URL",
|
||||
"Enter custom URL": "Ingrese una URL personalizada",
|
||||
"Enter URL": "Escriba la URL",
|
||||
|
||||
"Advanced": "Avanzado",
|
||||
"Advanced Options": "Opciones avanzadas",
|
||||
"Logging": "Registro",
|
||||
"Replace hbmenu on exit": "Reemplazar hbmenu",
|
||||
"Restore hbmenu?": "¿Restaurar hbmenu?",
|
||||
"Restore": "Restaurar",
|
||||
"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 find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Fallo al encontrar /switch/hbmenu.nro\nUse la tienda para reinstalar hbmenu",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Fallo al restaurar hbmenu. Por favor vuelva 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",
|
||||
"Restart Sphaira?": "¿Reiniciar sphaira?",
|
||||
"Press OK to restart Sphaira": "Presiona OK para reiniciar sphaira",
|
||||
"Boost CPU during transfer": "Tranferir con overclock",
|
||||
"Text scroll speed": "Velocidad de scroll",
|
||||
"Slow": "Lento",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Rápido",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
"Enable sysmmc": "",
|
||||
"Enable emummc": "",
|
||||
"Show install warning": "Precaución de instalación",
|
||||
"Set left-side menu": "Menú izquierdo",
|
||||
"Set right-side menu": "Menú derecho",
|
||||
"Install options": "Opciones de instalación",
|
||||
"Install Options": "Opciones de instalación",
|
||||
"Enable sysmmc": "Habilitar SysMMC",
|
||||
"Enable emummc": "Habilitar EmuMMC",
|
||||
"Show install warning": "Mostrar precaución al instalar",
|
||||
"Install location": "Dispositivo de instalación",
|
||||
"System memory": "Memoria de sistema",
|
||||
"microSD card": "microSD",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
"Skip base": "",
|
||||
"Skip patch": "",
|
||||
"Skip dlc": "",
|
||||
"Skip data patch": "",
|
||||
"Skip ticket": "",
|
||||
"Skip NCA hash verify": "",
|
||||
"Skip RSA header verify": "",
|
||||
"Skip RSA NPDM verify": "",
|
||||
"Ignore distribution bit": "",
|
||||
"Convert to standard crypto": "",
|
||||
"Lower master key": "",
|
||||
"Lower system version": "",
|
||||
"Allow downgrade": "Permitir instalar versiones anteriores",
|
||||
"Skip if already installed": "Saltar si ya está instalado",
|
||||
"Ticket only": "Únicamente tickets",
|
||||
"Skip base": "Saltar base",
|
||||
"Skip patch": "Saltar parche",
|
||||
"Skip dlc": "Saltar DLC",
|
||||
"Skip data patch": "Saltar datos de parche",
|
||||
"Skip ticket": "Saltar ticket",
|
||||
"Skip NCA hash verify": "Saltar verificación de hash NCA",
|
||||
"Skip RSA header verify": "Saltar verificación de encabezado RSA",
|
||||
"Skip RSA NPDM verify": "Saltar verificación de NPDM RSA",
|
||||
"Ignore distribution bit": "Ignorar bit de distribución",
|
||||
"Convert to standard crypto": "Convertir a crypto estándar",
|
||||
"Lower master key": "Bajar versión de llave maestra",
|
||||
"Lower system version": "Bajar versión de sistema",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Apps": "Apps",
|
||||
"Homebrew Options": "Opciones de Homebrew",
|
||||
"Homebrew Options": "Opciones de homebrew",
|
||||
"Hide Sphaira": "Ocultar Sphaira",
|
||||
"Install Forwarder": "Instalar Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ADVERTENCIA: ¡La instalación de fordwarders podría producir un baneo de la consola!",
|
||||
"Installing Forwarder": "Instalando Forwarder",
|
||||
"Creating Program": "Creando Program",
|
||||
"Creating Control": "Creando Control",
|
||||
"Creating Meta": "Creando Meta",
|
||||
"Install Forwarder": "Instalar forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ADVERTENCIA: ¡la instalación de fordwarders podría terminar en un baneo!",
|
||||
"Installing Forwarder": "Instalando forwarder",
|
||||
"Creating Program": "Creando program",
|
||||
"Creating Control": "Creando control",
|
||||
"Creating Meta": "Creando meta",
|
||||
"Writing Nca": "Creando NCA",
|
||||
"Updating ncm databse": "Actualizando base de datos ncm",
|
||||
"Updating ncm database": "Actualizando base de datos ncm",
|
||||
"Pushing application record": "Registro de aplicación",
|
||||
"Failed to install forwarder": "Fallo al instalar forwarder",
|
||||
"Unstarred ": "Quitar Favorito",
|
||||
"Unstar": "Quitar de favoritos",
|
||||
"Star": "Añadir a favoritos",
|
||||
"Unstarred ": "No favorito",
|
||||
"Starred ": "Favorito",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Error al remover forwarder anterior, por favor remuévalo manualmente",
|
||||
|
||||
"AppStore": "Tienda",
|
||||
"Appstore": "",
|
||||
"Appstore": "Tienda",
|
||||
"Store": "Tienda",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtrar: %s | Clasificar: %s | Orden: %s",
|
||||
"AppStore Options": "Opciones de la Tienda",
|
||||
"AppStore Options": "Opciones de la tienda",
|
||||
"Info": "Información",
|
||||
"Changelog": "Log de cambios",
|
||||
"Changelog": "Registro de cambios",
|
||||
"Details": "Detalles",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "actualizado: %s",
|
||||
"category: %s": "categoría: %s",
|
||||
"extracted: %.2f MiB": "extraído: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "Mostrar mas del Autor",
|
||||
"Leave Feedback": "Dejar Mensaje",
|
||||
"More by Author": "Mostrar mas del autor",
|
||||
"Leave Feedback": "Dejar mensaje",
|
||||
|
||||
"FileBrowser": "Explorador de archivos",
|
||||
"Files": "Archivos",
|
||||
@@ -257,69 +280,75 @@
|
||||
"Copy": "Copiar",
|
||||
"Copying ": "Copiando ",
|
||||
"Paste": "Pegar",
|
||||
"Paste ": "Pegar ",
|
||||
" file(s)?": " ¿archivo(s)?",
|
||||
"Paste file(s)?": "Pegar archivo(s)",
|
||||
"Pasting ": "Pegando ",
|
||||
"Pasting": "Pegando",
|
||||
"Rename": "Renombrar",
|
||||
"Set New File Name": "Establecer nuevo nombre de archivo",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
"Extract to root": "",
|
||||
"Are you sure you want to extract to root?": "",
|
||||
"Extract to...": "",
|
||||
"Enter the path to the folder to extract into": "",
|
||||
"Extracting ": "",
|
||||
"Extract success!": "",
|
||||
"Extract failed!": "",
|
||||
"Compress to zip": "",
|
||||
"Compress Options": "",
|
||||
"Compress": "",
|
||||
"Compress to...": "",
|
||||
"Compressing ": "",
|
||||
"Compress success!": "",
|
||||
"Compress failed!": "",
|
||||
"Failed to delete directory": "Error al borrar carpeta",
|
||||
"Failed to delete file": "Error al borrar archivo",
|
||||
"Extract zip": "Extraer carpeta comprimida ZIP",
|
||||
"Extract Options": "Opciones de extracción",
|
||||
"Extract here": "Extraer aquí",
|
||||
"Extract to root": "Extraer en la raíz",
|
||||
"Are you sure you want to extract to root?": "¿Realmente desea extraerlo en la carpeta raíz?",
|
||||
"Extract to...": "Extraer en…",
|
||||
"Enter the path to the folder to extract into": "Ingrese la ruta en la cual extraer",
|
||||
"Extracting ": "Extrayendo",
|
||||
"Extract success!": "¡Extracción satisfactoria!",
|
||||
"Extract failed!": "¡Extracción fallida!",
|
||||
"Compress to zip": "Comprimir en archivo ZIP",
|
||||
"Compress Options": "Opciones de compresión",
|
||||
"Compress": "Comprimir",
|
||||
"Compress to...": "Comprimir en…",
|
||||
"Compressing ": "Comprimiendo",
|
||||
"Compress success!": "¡Compresión satisfactoria!",
|
||||
"Compress failed!": "¡Error en la descompresión!",
|
||||
"Create File": "Crear archivo",
|
||||
"Set File Name": "Establecer nombre de archivo",
|
||||
"Create Folder": "Crear carpeta",
|
||||
"Set Folder Name": "Establecer nombre de carpeta",
|
||||
"Creating ": "Creando ",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Ver como texto (sin terminar)",
|
||||
"Upload": "Subir",
|
||||
"Select upload location": "Seleccione la ubicación a la cual subir",
|
||||
"No upload locations set!": "No se ha definido la ubicación para subir",
|
||||
"Uploading": "Subiendo",
|
||||
"Upload successfull!": "¡Subida satisfactoria!",
|
||||
"Upload failed!": "¡Error en la subida!",
|
||||
"Hash": "Hash",
|
||||
"Hash Options": "Opciones de hash",
|
||||
"Hashing": "Creando hash",
|
||||
"Failed to hash file...": "Error al crear hash del archivo…",
|
||||
"Ignore read only": "Ignorar sólo lectura",
|
||||
"Mount": "Montar",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Imagen memoria interna",
|
||||
"Image microSD card": "Imagen tarjeta microSD",
|
||||
"Empty...": "Vacío...",
|
||||
"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: ",
|
||||
"Close FileBrowser?": "¿Cerrar explorador de archivos?",
|
||||
|
||||
"Sort By": "Ordenar por",
|
||||
"Sort Options": "Opciones de clasificación",
|
||||
"Filter": "Filtrar",
|
||||
"Filter": "Filtro",
|
||||
"All": "Todo",
|
||||
"Emulators": "Emuladores",
|
||||
"Tools": "Herramientas",
|
||||
"Themes": "Temas",
|
||||
"Legacy": "Legado",
|
||||
"Sort": "Clasificar",
|
||||
"Sort": "Ordenar",
|
||||
"Size": "Tamaño",
|
||||
"Size (Star)": "Tamaño (favorito)",
|
||||
"Size (Star)": "Tamaño (favoritos)",
|
||||
"Alphabetical": "Alfabético",
|
||||
"Alphabetical (Star)": "Alfabético (favorito)",
|
||||
"Alphabetical (Star)": "Alfabético (favoritos)",
|
||||
"Updated": "Actualizado",
|
||||
"Updated (Star)": "Actualizado (favorito)",
|
||||
"Updated (Star)": "Actualizado (favoritos)",
|
||||
"Downloads": "Descargas",
|
||||
"Likes": "Me Gusta",
|
||||
"Likes": "Me gusta",
|
||||
"ID": "ID",
|
||||
"Order": "Orden",
|
||||
"Descending": "Descendente",
|
||||
@@ -328,51 +357,52 @@
|
||||
"Ascending": "Ascendente",
|
||||
"Ascending (Up)": "Ascendente (arriba)",
|
||||
"Asc": "Ascendente",
|
||||
"Layout": "",
|
||||
"List": "",
|
||||
"Icon": "",
|
||||
"Grid": "",
|
||||
"Layout": "Diseño",
|
||||
"List": "Lista",
|
||||
"Icon": "Íconos",
|
||||
"Grid": "Malla",
|
||||
"Search": "Buscar",
|
||||
|
||||
"Options": "Opciones",
|
||||
"Split": "Dividir",
|
||||
"OK": "OK",
|
||||
"Back": "Atrás",
|
||||
"Select": "Seleccionar",
|
||||
"Open": "Abrir",
|
||||
"Close": "Cerrar",
|
||||
"Launch": "Ejecutar",
|
||||
"Restart": "Reiniciar",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "Quitar favorito",
|
||||
"Star": "Favorito",
|
||||
"Next": "Siguiente",
|
||||
"Prev": "Anterior",
|
||||
"Yes": "Sí",
|
||||
"No": "No",
|
||||
"On": "",
|
||||
"Off": "",
|
||||
"On": "Activo",
|
||||
"Off": "Apagado",
|
||||
|
||||
"Install": "Instalar",
|
||||
"Install Selected files?": "",
|
||||
"Install Selected files?": "¿Instalar los archivos seleccionados?",
|
||||
"Installing ": "Instalando ",
|
||||
"Installed ": "",
|
||||
"Installed ": "Instalado",
|
||||
"Installed!": "¡Instalado!",
|
||||
"Trying to load ": "Intentando cargar ",
|
||||
"Checking MD5": "Chequeando MD5",
|
||||
"Checking MD5": "Revisando MD5",
|
||||
|
||||
"Delete": "Borrar",
|
||||
"Delete Selected files?": "¿Eliminar archivos seleccionados?",
|
||||
"Are you sure you want to delete ": "¿Estás seguro que quieres eliminar? ",
|
||||
"Are you sure you want to delete ": "¿Realmente desea eliminar? ",
|
||||
"Scanning ": "Escaneando ",
|
||||
"Deleting ": "Borrando ",
|
||||
"Deleting": "Borrando",
|
||||
"Remove": "Borrar",
|
||||
"Completely remove ": "Eliminar completamente",
|
||||
"Removing ": "Removiendo ",
|
||||
"Removed ": "Removido ",
|
||||
"Uninstalling ": "Desinstalando ",
|
||||
"Removed ": "Removido ",
|
||||
|
||||
"Download": "Descargar",
|
||||
"Downloading ": "Descargando ",
|
||||
"Downloaded ": "Descargado ",
|
||||
"Download via the Network options!": "¡Descargar vía las opciones de red!",
|
||||
|
||||
"Update": "Actualizar",
|
||||
"Update avaliable: ": "Actualización disponible: ",
|
||||
@@ -380,13 +410,13 @@
|
||||
"Updated to ": "Actualizado a ",
|
||||
"Failed to download update": "Fallo al descargar actualización",
|
||||
|
||||
"%zu hours %zu minutes remaining": "",
|
||||
"%zu minutes %zu seconds remaining": "",
|
||||
"%zu seconds remaining": "",
|
||||
"%zu hours %zu minutes remaining": "Faltan %zu horas %zu minutos",
|
||||
"%zu minutes %zu seconds remaining": "Faltan %zu minutos %zu segundos",
|
||||
"%zu seconds remaining": "Faltan %zu segundos",
|
||||
|
||||
"Loading...": "Cargando...",
|
||||
"Loading...": "Cargando…",
|
||||
"Loading": "Cargando",
|
||||
"Empty!": "¡Vacío!",
|
||||
"Not Ready...": "No listo aún...",
|
||||
"Not Ready...": "Todavía no está listo…",
|
||||
"Error loading page!": "¡Error cargando la página!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
{
|
||||
"[Applet Mode]": "[Mode Applet]",
|
||||
"No Internet": "Pas d'Internet",
|
||||
"Switch-Handheld!": "Switch-Portable",
|
||||
"Switch-Docked!": "Switch-Dockée",
|
||||
"Audio disabled due to suspended game": "Audio désactivé à cause d'un jeu suspendu",
|
||||
"Are you sure you wish to cancel?": "Souhaitez-vous vraiment annuler?",
|
||||
"Switch-Handheld!": "Mode Portable",
|
||||
"Switch-Docked!": "Mode Dockée",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "Attention ! Les journaux sont activés, Sphaira fonctionnera lentement !",
|
||||
"Audio disabled due to suspended game": "Audio désactivé à cause d'un jeu en pause",
|
||||
"Are you sure you wish to cancel?": "Souhaitez-vous vraiment annuler ?",
|
||||
"An error occurred": "Une erreur s'est produite",
|
||||
"If this message appears repeatedly, please open an issue.": "Si ce message apparait en boucle veuillez ouvrir une issue.",
|
||||
"If this message appears repeatedly, please open an issue.": "Si ce message apparaît en boucle, veuillez ouvrir une issue sur :",
|
||||
|
||||
"Menu Options": "Options des Menus",
|
||||
"Menu": "Menu",
|
||||
"Theme": "Thème",
|
||||
"Theme Options": "Options de Thème",
|
||||
"Select Theme": "Choisir un Thème",
|
||||
"Menu Options": "Paramètres du menu",
|
||||
"Menu": "Paramètres",
|
||||
"Theme": "Thèmes",
|
||||
"Theme Options": "Paramètres des Thèmes",
|
||||
"Select Theme": "Choisir un thème",
|
||||
"Music": "Musique",
|
||||
"12 Hour Time": "Temps sur 12 heures",
|
||||
"12 Hour Time": "Format horloge 12 heures",
|
||||
"Download Default Music": "Télécharger la musique par défaut",
|
||||
"Failed to download default_music.bfstm, please try again": "Echec du téléchargement de default_music.bfstm, veuillez réessayer",
|
||||
"Overwrite current default music?": "Remplacer la musique actuelle par défaut?",
|
||||
"Failed to download default_music.bfstm, please try again": "Échec du téléchargement de default_music.bfstm, veuillez réessayer",
|
||||
"Overwrite current default music?": "Remplacer la musique actuelle par défaut ?",
|
||||
|
||||
"Network": "Réseau",
|
||||
"Network Options": "Options Réseau",
|
||||
"Network Options": "Paramètres Réseau",
|
||||
"Ftp": "FTP",
|
||||
"Mtp": "MTP",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink Connecté",
|
||||
"Nxlink Upload": "Nxlink téléversement",
|
||||
"Nxlink Upload": "Téléversement Nxlink",
|
||||
"Nxlink Finished": "Nxlink terminé",
|
||||
"Hdd": "Disque Dur",
|
||||
"Hdd write protect": "Protection en écriture du disque dur",
|
||||
|
||||
"Language": "Langue",
|
||||
"Auto": "Auto",
|
||||
@@ -47,65 +50,79 @@
|
||||
|
||||
"Misc": "Divers",
|
||||
"Misc Options": "Options Diverses",
|
||||
"Games": "Jeux",
|
||||
"Game Options": "Options de jeu",
|
||||
"Games": "Jeux installés",
|
||||
"Game Options": "Options des jeux",
|
||||
"Hide forwarders": "Masquer les forwarders",
|
||||
"Launch random game": "Lancer un jeu au hasard",
|
||||
"List meta records": "Lister les enregistrements meta",
|
||||
"Entries": "Entrées",
|
||||
"List meta records": "Lister les méta-enregistrements",
|
||||
"Entries": "Meta disponible",
|
||||
"Failed to list application meta entries": "Le listage des entrées meta de l'application a échoué",
|
||||
"No meta entries found...\n": "Aucune entrée meta trouvée...\n",
|
||||
"Updating application record list": "Mise à jour de la liste d'enregistrement de l'application",
|
||||
"Dump": "Dumper",
|
||||
"Select content to dump": "Sélectionner un contenu à dumper",
|
||||
"Dump options": "Option du Dumper",
|
||||
"Dump options": "Paramètres",
|
||||
"Select content to dump": "Contenu à dumper",
|
||||
"Dump All": "Tout dumper",
|
||||
"Dump Application": "Dumper Application",
|
||||
"Dump Patch": "Dumper mise à jour",
|
||||
"Dump AddOnContent": "Dumper DLCs",
|
||||
"Dump DataPatch": "Dumper patch de données",
|
||||
"Select dump location": "Sélectionner l'emplacement du dump",
|
||||
"microSD card (/dumps/NSP/)": "carte microSD (/dumps/NSP/)",
|
||||
"Dump Application": "Dumper uniquement l'application (jeu)",
|
||||
"Dump Patch": "Dumper uniquement la mise à jour",
|
||||
"Dump AddOnContent": "Dumper uniquement le(s) DLCs",
|
||||
"Dump DataPatch": "Dumper le patch de données",
|
||||
"Created nested folder": "Créer un dossier imbriqué",
|
||||
"Append folder with .xci": "Ajouter un dossier avec .xci",
|
||||
"Trim XCI": "Trimmer le XCI",
|
||||
"Label trimmed XCI": "Étiquette sur le XCI trimmer",
|
||||
"Multi-threaded USB transfer": "Transfert USB multithread",
|
||||
"Dump All Bins": "Dumper tous les éléments",
|
||||
"Dump XCI": "Dumper le XCI",
|
||||
"Dump Card ID Set": "Dumper l'ID de la cartouche",
|
||||
"Dump Card UID": "Dumper l'UID de la cartouche",
|
||||
"Dump Certificate": "Dumper le certificat",
|
||||
"Dump Initial Data": "Dumper l'Initial Data",
|
||||
"Select dump location": "Sélectionner l'emplacement du dump :",
|
||||
"microSD card (/dumps/)": "carte microSD (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "Transfert USB (Switch à Switch",
|
||||
"/dev/null (Speed Test)": "/dev/null (test de vitesse)",
|
||||
"Dumping": "Dump en cours",
|
||||
"Dump successfull!": "Dump réussi!",
|
||||
"Dump failed!": "Dump échoué!",
|
||||
"Dumping": "Dump en cours...",
|
||||
"Dump successfull!": "Dump réussi !",
|
||||
"Dump failed!": "Échec du dump !",
|
||||
"Delete successful!": "Fichier supprimé !",
|
||||
"Delete failed!": "Échec de la suppression !",
|
||||
"Success": "Succès",
|
||||
"Delete successfull!": "Suprression réussie!",
|
||||
"Delete failed!": "Suprression échouée!",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Options Themezer",
|
||||
"Nsfw": "Nsfw",
|
||||
"Themezer": "Themezer (thèmes Switch)",
|
||||
"Themezer Options": "Paramètres",
|
||||
"Nsfw": "Contenu NSFW",
|
||||
"Page": "Page",
|
||||
"Page %zu / %zu": "Page %zu / %zu",
|
||||
"Enter Page Number": "Entrez Un Numéro De Page",
|
||||
"Enter Page Number": "Numéro de la page :",
|
||||
"Bad Page": "Page inexistante",
|
||||
"Download theme?": "Télécharger le thème?",
|
||||
"Download theme?": "Voulez-vous télécharger ce thème ?",
|
||||
|
||||
"GitHub": "GitHub",
|
||||
"GitHub": "Github",
|
||||
"Downloading json": "Téléchargement du json",
|
||||
"Select asset to download for ": "Sélectionner l'asset à télécharger pour ",
|
||||
"Select asset to download for ": "Sélectionnez l'actif à télécharger pour :",
|
||||
"Failed to download json": "Échec du téléchargement du json",
|
||||
"Failed to download app!": "Échec du téléchargement de l'application",
|
||||
|
||||
"FTP Install": "Installation via FTP",
|
||||
"FTP Install (EXPERIMENTAL)": "Installation via FTP (EXPERIMENTAL)",
|
||||
"Connection Type: WiFi | Strength: ": "Type de connexion: WiFi, force du signal: ",
|
||||
"Connection Type: Ethernet": "Type de connexion: Ethernet",
|
||||
"Connection Type: None": "Type de connexion: Aucune",
|
||||
"Host:": "Hôte:",
|
||||
"Port:": "Port:",
|
||||
"Username:": "Nom d'utilisateur:",
|
||||
"Password:": "Mot de passe:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "Mot de passe:",
|
||||
"Failed to install via FTP, press B to exit...": "Installation via FTP échouée, appuyer sur B pour quitter...",
|
||||
"Ftp install success!": "Installation via FTP réussie!",
|
||||
"Ftp install failed!": "Installation via FTP échouée!",
|
||||
"USB Install": "Installation via USB",
|
||||
"USB": "USB",
|
||||
"FTP Install": "Installer contenu via FTP",
|
||||
"Connection Type: WiFi | Strength: ": "Type de connexion : WiFi / Force du signal :",
|
||||
"Connection Type: Ethernet": "Type de connexion : Ethernet",
|
||||
"Connection Type: None": "Type de connexion : Aucune",
|
||||
"Host:": "Hôte :",
|
||||
"Port:": "Port :",
|
||||
"Username:": "Nom d'utilisateur :",
|
||||
"Password:": "Mot de passe :",
|
||||
"SSID:": "SSID :",
|
||||
"Passphrase:": "Mot de passe :",
|
||||
"Failed to install, press B to exit...": "Installation échouée, appuyer sur B pour quitter...",
|
||||
"Install success!": "Installation réussie !",
|
||||
"Install failed!": "Installation échouée !",
|
||||
"USB Install": "Installer contenu via USB",
|
||||
"USB": "Installation via USB",
|
||||
"Connected, waiting for file list...": "Connecté, en attente de la liste des fichiers...",
|
||||
"Connected, starting transfer...": "Connecté, début du transfère...",
|
||||
"Failed to init usb, press B to exit...": "Echec de l'initialisation de l'USB, appuyer sur B pour quitter...",
|
||||
"Failed to init usb, press B to exit...": "Échec de l'initialisation de l'USB, appuyer sur B pour quitter...",
|
||||
"Waiting for connection...": "En attente de la connexion...",
|
||||
"Transferring data...": "Transfère de données...",
|
||||
"USB connected, sending file list": "USB connecté, envoi de la liste des fichiers",
|
||||
@@ -114,29 +131,32 @@
|
||||
"Disable MTP for usb install": "Désactivation du MTP pour l'installation via USB",
|
||||
"Re-enabled MTP": "Réactivation du MTP",
|
||||
"Installed via usb": "Installé via USB",
|
||||
"Usb install success!": "Installation via USB réussie!",
|
||||
"Usb install failed!": "Installation via USB échouée!",
|
||||
"Usb install success!": "Installation via USB réussie !",
|
||||
"Usb install failed!": "Installation via USB échouée !",
|
||||
"Press B to exit...": "Appuyer sur B pour quitter...",
|
||||
"GameCard Install": "Installation de la cartouche",
|
||||
"GameCard": "Cartouche",
|
||||
"GameCard Install": "Installation de la cartouche du jeu",
|
||||
"GameCard": "Cartouche du jeu",
|
||||
"GC": "GC",
|
||||
"System memory %.1f GB": "Mémoire système %.1f GB",
|
||||
"microSD card %.1f GB": "Carte microSD %.1f GB",
|
||||
"Nand Install": "Installer sur la Nand",
|
||||
"SD Card Install": "Installer sur la carte SD",
|
||||
"Exit": "Quitter",
|
||||
"Gc install success!": "Installation de la cartouche réussie!",
|
||||
"Gc install failed!": "Installation de la cartouche échouée!",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Installation désactivée...\nVeuillez activer l'installation via les options d'installation.",
|
||||
"No GameCard inserted": "Aucune cartouche de jeu insérée !",
|
||||
"GameCard is already trimmed!": "La carte du jeu est déjà trimmer !",
|
||||
"WARNING: GameCard is already trimmed!": "AVERTISSEMENT : la carte du jeu est déjà trimmer ! ",
|
||||
"Continue": "Continuer",
|
||||
"Gc install success!": "Installation de la cartouche réussie !",
|
||||
"Gc install failed!": "Installation de la cartouche échouée !",
|
||||
|
||||
"IRS (Infrared Joycon Camera)": "IRS (infrarouge de la caméra du Joycon",
|
||||
"IRS": "IRS",
|
||||
"IRS (Infrared Joycon Camera)": "IRS (infrarouge de la cam du Joycon",
|
||||
"IRS": "Caméra Infrarouge",
|
||||
"Irs": "Irs",
|
||||
"Ambient Noise Level: ": "Niveau De Bruit Ambiant: ",
|
||||
"Ambient Noise Level: ": "Niveau de bruit ambiant: ",
|
||||
"Controller": "Contrôleur",
|
||||
"Pad ": "Manette ",
|
||||
"HandHeld": "Portable",
|
||||
" (Available)": " (Disponible)",
|
||||
" (Unsupported)": "Non supporté",
|
||||
"Pad ": "Manette",
|
||||
"HandHeld": "Joycon",
|
||||
" (Available)": " (Connectée)",
|
||||
" (Unsupported)": "Non supportée",
|
||||
" (Unconnected)": " (Non connectée)",
|
||||
"Rotation": "Rotation",
|
||||
"0 (Sideways)": "0 (Paysage)",
|
||||
@@ -155,59 +175,61 @@
|
||||
"Dim group": "Groupe sombre",
|
||||
"None": "Aucun",
|
||||
"Gain": "Gain",
|
||||
"Negative Image": "Image Négative",
|
||||
"Normal image": "Image normale",
|
||||
"Negative image": "Image négative",
|
||||
"Format": "Format",
|
||||
"Trimming Format": "Format de Découpe",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Filtre de Lumière Externe",
|
||||
"Load Default": "Charger par Défaut",
|
||||
"Negative Image": "Format d'image",
|
||||
"Normal image": "Normale",
|
||||
"Negative image": "Négative",
|
||||
"Format": "Format d'image",
|
||||
"Trimming Format": "Format du découpage",
|
||||
"External Light Filter": "Filtre de lumière externe",
|
||||
"Load Default": "Rétablir par Défaut",
|
||||
|
||||
"Advanced": "Avancé",
|
||||
"Advanced Options": "Options Avancées",
|
||||
"Logging": "Journalisation",
|
||||
"Replace hbmenu on exit": "Remplacer hbmenu quand quitté",
|
||||
"Restore hbmenu?": "Restaurer hbmenu?",
|
||||
"Restore": "Restaurer",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "/switch/hbmenu.nro n'a pas été trouvé\nUtiliser l'Appstore pour réinstaller le hbmenu",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Echec de la restauration de hbmenu, veuillez le réinstaller",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Echec de la restauration de hbmenu, sphaira sera utilisé à la place",
|
||||
"Restored hbmenu, closing sphaira": "Hbmenu restauré, fermeture de sphaira",
|
||||
"Restored hbmenu": "Hbmenu restauré",
|
||||
"Restart Sphaira?": "Redémarrer Sphaira?",
|
||||
"Web": "Web",
|
||||
"Select URL": "Sélectionnez une adresse",
|
||||
"Enter custom URL": "Entrez une adresse personnalisée",
|
||||
"Enter URL": "Entrer l'adresse :",
|
||||
|
||||
"Advanced": "Menu avancé",
|
||||
"Advanced Options": "Menu avancé",
|
||||
"Logging": "Fichier journal",
|
||||
"Replace hbmenu on exit": "Remplacer HBmenu par Sphaira",
|
||||
"Restore hbmenu?": "Restaurer HBmenu ?",
|
||||
"Restore": "Restaurer !",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "/switch/hbmenu.nro non trouvé\nRéinstaller le HBmenu via Appstore",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Échec de la restauration du HBmenu, veuillez le réinstaller",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Échec de la restauration de HBmenu, Sphaira sera utilisé à la place",
|
||||
"Restored hbmenu, closing sphaira": "Hbmenu restauré, fermeture de Sphaira",
|
||||
"Restored hbmenu": "HBmenu restauré !",
|
||||
"Restart Sphaira?": "Redémarrer Sphaira ?",
|
||||
"Press OK to restart Sphaira": "Appuyez sur OK pour redémarrer Sphaira",
|
||||
"Boost CPU during transfer": "Augmenter le CPU pendant le transfert",
|
||||
"Text scroll speed": "Vitesse de défilement du texte",
|
||||
"Slow": "Lent",
|
||||
"Normal": "Normal",
|
||||
"Fast": "Rapide",
|
||||
"Set right-side menu": "Configurer le menu de droite",
|
||||
"Install options": "Options d'installation",
|
||||
"Set left-side menu": "Menu de gauche par défaut",
|
||||
"Set right-side menu": "Menu de droite par défaut",
|
||||
"Install options": "Options d'installation des jeux",
|
||||
"Install Options": "Options d'Installation",
|
||||
"Enable sysmmc": "Activer sur la sysmmc",
|
||||
"Enable emummc": "Activer sur l'emummc",
|
||||
"Show install warning": "Afficher l'avertissement d'installation",
|
||||
"Enable sysmmc": "Afficher la SysNAND",
|
||||
"Enable emummc": "Afficher l'emuNAND",
|
||||
"Show install warning": "Avertissement lors d'installation",
|
||||
"Install location": "Emplacement d'installation",
|
||||
"System memory": "Mémoire système",
|
||||
"microSD card": "Carte microSD",
|
||||
"Boost CPU clock": "Augmenter la vitesse de l'horloge CPU",
|
||||
"Allow downgrade": "Autoriser le downgrade",
|
||||
"Skip if already installed": "Ignorer si déjà installé",
|
||||
"Ticket only": "Seulement le ticket",
|
||||
"Skip base": "Ignorer base",
|
||||
"Skip patch": "Ignorer mise à jour",
|
||||
"Skip dlc": "Ignorer DLC",
|
||||
"Skip data patch": "Ignorer patch de données",
|
||||
"Skip base": "Ignorer la base du jeu",
|
||||
"Skip patch": "Ignorer les mises à jour",
|
||||
"Skip dlc": "Ignorer le(s) DLC",
|
||||
"Skip data patch": "Ignorer le patch de données",
|
||||
"Skip ticket": "Ignorer ticket",
|
||||
"Skip NCA hash verify": "Ignorer la vérification du hash NCA",
|
||||
"Skip RSA header verify": "Ignorer la vérification de l'entête RSA",
|
||||
"Skip RSA NPDM verify": "Ignorer la vérification RSA NPDM",
|
||||
"Skip NCA hash verify": "Vérification du hash NCA",
|
||||
"Skip RSA header verify": "Vérification de l'entête RSA",
|
||||
"Skip RSA NPDM verify": "Vérification RSA NPDM",
|
||||
"Ignore distribution bit": "Ignorer le bit de distribution",
|
||||
"Convert to standard crypto": "Convertir vers la crypto standard",
|
||||
"Convert to common ticket": "Convertir en ticket commun",
|
||||
"Convert to standard crypto": "Convertir en crypto standard",
|
||||
"Lower master key": "Abaisser la master key",
|
||||
"Lower system version": "Abaisser la version du système",
|
||||
|
||||
@@ -215,34 +237,36 @@
|
||||
"Apps": "Applications",
|
||||
"Homebrew Options": "Options Homebrew",
|
||||
"Hide Sphaira": "Masquer Sphaira",
|
||||
"Install Forwarder": "Installer le Forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENTION: L'installation de forwarders entraînera un ban!",
|
||||
"Installing Forwarder": "Installation Du Forwarder",
|
||||
"Install Forwarder": "Créer un Forwarder (icône au menu)",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ATTENTION : L'installation de forwarders entraînera un ban !",
|
||||
"Installing Forwarder": "Installation du Forwarder...",
|
||||
"Creating Program": "Création de Program",
|
||||
"Creating Control": "Création de Control",
|
||||
"Creating Meta": "Création de Meta",
|
||||
"Writing Nca": "Ecriture NCA",
|
||||
"Updating ncm databse": "Mise à jour de ncm databse",
|
||||
"Writing Nca": "Écriture NCA",
|
||||
"Updating ncm database": "Mise à jour de la base de données ncm",
|
||||
"Pushing application record": "Ajout de l'enregistrement de l'application",
|
||||
"Failed to install forwarder": "Echec de l'installation du forwarder",
|
||||
"Unstarred ": "Retiré des favories ",
|
||||
"Starred ": "Ajouté aux favories ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Supression de l'ancien forwarder échouée, supprimez-le manuellement!",
|
||||
"Failed to install forwarder": "Échec de l'installation du forwarder",
|
||||
"Unstar": "Retirer des favories",
|
||||
"Star": "Ajouter aux favories",
|
||||
"Unstarred ": "Retiré des favories : ",
|
||||
"Starred ": "Ajouté aux favories : ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Suppression de l'ancien forwarder échouée, supprimez-le manuellement !",
|
||||
|
||||
"AppStore": "AppStore",
|
||||
"Appstore": "Magasin d'applications",
|
||||
"Store": "Magasin",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Filtre: %s | Tri: %s | Ordre: %s",
|
||||
"Appstore": "Boutique d'applications",
|
||||
"Store": "AppStore",
|
||||
"Filter: %s | Sort : %s | Order: %s" : "Filtre : %s | Tri : %s | Ordre : %s",
|
||||
"AppStore Options": "Options de l'AppStore",
|
||||
"Info": "Info.",
|
||||
"Changelog": "Journal des modifications",
|
||||
"Details": "Détails",
|
||||
"version: %s": "version: %s",
|
||||
"updated: %s": "Mis à jour: %s",
|
||||
"category: %s": "catégorie: %s",
|
||||
"version: %s": "version : %s",
|
||||
"updated: %s": "Mis à jour : %s",
|
||||
"category: %s": "catégorie : %s",
|
||||
"extracted: %.2f MiB": "Extrait: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "Plus de cet Auteur",
|
||||
"More by Author": "Plus de cet auteur",
|
||||
"Leave Feedback": "Laisser un avis",
|
||||
|
||||
"FileBrowser": "Explorateur de Fichiers",
|
||||
@@ -250,76 +274,82 @@
|
||||
"%zd files": "%zd fichiers",
|
||||
"%zd dirs": "%zd dossiers",
|
||||
"File Options": "Options de Fichier",
|
||||
"Show Hidden": "Afficher Masqués",
|
||||
"Show Hidden": "Afficher fichier masqué",
|
||||
"Folders First": "Dossiers en Premier",
|
||||
"Hidden Last": "Masqués en Dernier",
|
||||
"Cut": "Couper",
|
||||
"Copy": "Copier",
|
||||
"Copying ": "Copie en cours ",
|
||||
"Copying ": "Copie en cours...",
|
||||
"Paste": "Coller",
|
||||
"Paste ": "Coller ",
|
||||
" file(s)?": " fichier(s)?",
|
||||
"Pasting ": "Collage en cours ",
|
||||
"Pasting": "Collage en cours",
|
||||
"Paste file(s)?": "Copier le(s) fichier(s) ?",
|
||||
"Pasting": "Collage en cours...",
|
||||
"Pasting": "Collage en cours...",
|
||||
"Rename": "Renommer",
|
||||
"Set New File Name": "Nouveau Nom Du Fichier",
|
||||
"Set New File Name": "Nouveau nom du fichier",
|
||||
"Failed to delete directory": "Échec de la suppression du répertoire",
|
||||
"Failed to delete file": "Échec de la suppression du fichier",
|
||||
"Extract zip": "Extraire le zip",
|
||||
"Extract Options": "Options d'extraction",
|
||||
"Extract here": "Extraire ici",
|
||||
"Extract to root": "Extraire à la racine",
|
||||
"Are you sure you want to extract to root?": "Souhaitez-vous vraiment extraire à la racine?",
|
||||
"Are you sure you want to extract to root?": "Souhaitez-vous vraiment extraire à la racine ?",
|
||||
"Extract to...": "Extraire vers...",
|
||||
"Enter the path to the folder to extract into": "Entrer le chemin du répertoire vers lequel extraire",
|
||||
"Extracting ": "Extraction en cours ",
|
||||
"Extract success!": "Extraction réussie!",
|
||||
"Extract failed!": "Extraction échouée!",
|
||||
"Extracting ": "Extraction en cours...",
|
||||
"Extract success!": "Extraction réussie !",
|
||||
"Extract failed!": "Extraction échouée !",
|
||||
"Compress to zip": "Compresser en zip",
|
||||
"Compress Options": "Options de compression",
|
||||
"Compress": "Compresser",
|
||||
"Compress to...": "Compresser vers...",
|
||||
"Compressing ": "Compression en cours ",
|
||||
"Compress success!": "Compression réussie!",
|
||||
"Compress failed!": "Compression échouée!",
|
||||
"Create File": "Créer un Fichier",
|
||||
"Set File Name": "Nommer Le Fichier",
|
||||
"Create Folder": "Créer un Dossier",
|
||||
"Set Folder Name": "Nommer Le Dossier",
|
||||
"Compressing ": "Compression en cours...",
|
||||
"Compress success!": "Compression réussie !",
|
||||
"Compress failed!": "Compression échouée !",
|
||||
"Create File": "Créer un fichier",
|
||||
"Set File Name": "Nommer Le fichier",
|
||||
"Create Folder": "Créer un dossier",
|
||||
"Set Folder Name": "Nommer le dossier",
|
||||
"Creating ": "Création ",
|
||||
"Upload": "Upload",
|
||||
"Select upload location": "Sélectionner l'emplacement d'upload",
|
||||
"No upload locations set!": "Aucun emplacement d'upload configuré!",
|
||||
"Uploading": "Upload en cours",
|
||||
"Upload successfull!": "Upload réussi!",
|
||||
"Upload failed!": "Upload échoué!",
|
||||
"View as text (unfinished)": "Afficher sous forme de texte (inachevé)",
|
||||
"Upload": "Téléverser",
|
||||
"Select upload location": "Emplacement du téléversement :",
|
||||
"No upload locations set!": "Aucun emplacement de téléversement configuré !",
|
||||
"Uploading": "Téléversement en cours...",
|
||||
"Upload successfull!": "Téléversement réussi !",
|
||||
"Upload failed!": "Téléversement échoué !",
|
||||
"Hash": "Hash",
|
||||
"Hash Options": "Options du Hash",
|
||||
"Hashing": "Hachage",
|
||||
"Failed to hash file...": "Échec du hachage du fichier...",
|
||||
"Ignore read only": "Ignorer lecture seule",
|
||||
"Mount": "Monter",
|
||||
"Sd": "Sd",
|
||||
"Image System memory": "Image de la mémoire System",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Image de la mémoire Système",
|
||||
"Image microSD card": "Image de la Carte microSD",
|
||||
"Empty...": "Vide...",
|
||||
"Open with DayBreak?": "Ouvrir avec DayBreak?",
|
||||
"Open with DayBreak?": "Ouvrir avec DayBreak ?",
|
||||
"Launch ": "Lancer ",
|
||||
"Launch option for: ": "Option de lancement pour: ",
|
||||
"Select launcher for: ": "Sélectionner le lanceur pour: ",
|
||||
"Launch option for: ": "Option de lancement pour : ",
|
||||
"Select launcher for: ": "Sélectionner le lanceur pour : ",
|
||||
"Close FileBrowser?": "Fermer le navigateur de fichiers ?",
|
||||
|
||||
"Sort By": "Tri Par",
|
||||
"Sort Options": "Options de Tri",
|
||||
"Sort By": "Trier par",
|
||||
"Sort Options": "Options de tri",
|
||||
"Filter": "Filtre",
|
||||
"All": "Tous",
|
||||
"Emulators": "Émulateurs",
|
||||
"Tools": "Outils",
|
||||
"Themes": "Thèmes",
|
||||
"Legacy": "Legacy",
|
||||
"Sort": "Tri",
|
||||
"Sort": "Trier",
|
||||
"Size": "Taille",
|
||||
"Size (Star)": "Taille (Favories)",
|
||||
"Size (Star)": "Taille (Favoris)",
|
||||
"Alphabetical": "Alphabétique",
|
||||
"Alphabetical (Star)": "Alphabétique (Favories)",
|
||||
"Alphabetical (Star)": "Alphabétique (Favoris)",
|
||||
"Updated": "Mis à jour",
|
||||
"Updated (Star)": "Mis à jour (Favories)",
|
||||
"Updated (Star)": "Mis à jour (Favoris)",
|
||||
"Downloads": "Téléchargements",
|
||||
"Likes": "Likes",
|
||||
"Likes": "Aimer",
|
||||
"ID": "ID",
|
||||
"Order": "Ordre",
|
||||
"Descending": "Décroissant",
|
||||
@@ -332,61 +362,62 @@
|
||||
"List": "Liste",
|
||||
"Icon": "Icône",
|
||||
"Grid": "Grille",
|
||||
"Search": "Recherche",
|
||||
"Search": "Lancer la recherche",
|
||||
|
||||
"Options": "Options",
|
||||
"Split": "Découper",
|
||||
"OK": "OK",
|
||||
"Back": "Retour",
|
||||
"Select": "Sélectionner",
|
||||
"Open": "Ouvrir",
|
||||
"Close": "Fermer",
|
||||
"Launch": "Exécuter",
|
||||
"Restart": "Redémarrer",
|
||||
"Next": "Suivant",
|
||||
"Prev": "Précédent",
|
||||
"Unstar": "Retirer des favories",
|
||||
"Star": "Ajouter aux favories",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"On": "On",
|
||||
"Off": "Off",
|
||||
|
||||
"Install": "Installer",
|
||||
"Install Selected files?": "Installer les fichiers sélectionnés?",
|
||||
"Installing ": "Installation en cours ",
|
||||
"Install Selected files?": "Installer les fichiers sélectionnés ?",
|
||||
"Installing ": "Installation en cours...",
|
||||
"Installed ": "Installé ",
|
||||
"Installed!": "Installé!",
|
||||
"Trying to load ": "Tente de charger ",
|
||||
"Installed!": "Installé !",
|
||||
"Trying to load ": "Essayer de charger ",
|
||||
"Checking MD5": "Vérification MD5",
|
||||
|
||||
"Delete": "Supprimer",
|
||||
"Delete Selected files?": "Supprimer les fichiers sélectionnés?",
|
||||
"Delete Selected files?": "Supprimer les fichiers sélectionnés ?",
|
||||
"Are you sure you want to delete ": "Êtes-vous sûr de vouloir supprimer ",
|
||||
"Scanning ": "Scan en cours ",
|
||||
"Deleting ": "Suppression en cours ",
|
||||
"Deleting": "Suppression en cours",
|
||||
"Scanning ": "Scan en cours...",
|
||||
"Deleting ": "Suppression en cours...",
|
||||
"Deleting": "Suppression en cours...",
|
||||
"Remove": "Supprimer",
|
||||
"Completely remove ": "Supprimer totalement ",
|
||||
"Removing ": "Suppression en cours ",
|
||||
"Removing ": "Suppression en cours...",
|
||||
"Uninstalling ": "Désinstallation en cours...",
|
||||
"Removed ": "Supprimé ",
|
||||
"Uninstalling ": "Désinstallation en cours ",
|
||||
|
||||
"Download": "Télécharger",
|
||||
"Downloading ": "Téléchargement en cours ",
|
||||
"Downloaded ": "Téléchargé",
|
||||
"Downloading ": "Téléchargement en cours...",
|
||||
"Downloaded ": "Téléchargé ",
|
||||
"Download via the Network options!": "Téléchargez via les options réseau !",
|
||||
|
||||
"Update": "Mise à jour",
|
||||
"Update avaliable: ": "Mise à jour disponible: ",
|
||||
"Download update: ": "Télécharger la mise à jour: ",
|
||||
"Update avaliable: ": "Mise à jour disponible : ",
|
||||
"Download update: ": "Télécharger la mise à jour : ",
|
||||
"Updated to ": "Mis à jour vers ",
|
||||
"Failed to download update": "Echec du téléchargement de la mise à jour",
|
||||
"Failed to download update": "Échec du téléchargement de la mise à jour !",
|
||||
|
||||
"%zu hours %zu minutes remaining": "%zu heures %zu minutes restantes",
|
||||
"%zu minutes %zu seconds remaining": "%zu minutes %zu secondes restantes",
|
||||
"%zu seconds remaining": "%zu secondes restantes",
|
||||
|
||||
"Loading...": "Chargement...",
|
||||
"Loading": "Chargement en cours",
|
||||
"Empty!": "Vide!",
|
||||
"Not Ready...": "Pas prêt",
|
||||
"Loading": "Chargement en cours...",
|
||||
"Empty!": "Vide !",
|
||||
"Not Ready...": "Pas prêt !",
|
||||
"Error loading page!": "Erreur du chargement de la page!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "Niente Internet",
|
||||
"Switch-Handheld!": "Switch Portatile",
|
||||
"Switch-Docked!": "Switch Dock",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "",
|
||||
"Audio disabled due to suspended game": "Audio disabilitato poichè un app è in pausa",
|
||||
"Are you sure you wish to cancel?": "Sei sicuro di voler annullare?",
|
||||
"An error occurred": "",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink connesso",
|
||||
"Nxlink Upload": "Nxlink upload",
|
||||
"Nxlink Finished": "Nxlink finito",
|
||||
"Hdd": "",
|
||||
"Hdd write protect": "",
|
||||
|
||||
"Language": "Lingua",
|
||||
"Auto": "Auto",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Dump options": "",
|
||||
"Dump Options": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Created nested folder": "",
|
||||
"Append folder with .xci": "",
|
||||
"Trim XCI": "",
|
||||
"Label trimmed XCI": "",
|
||||
"Multi-threaded USB transfer": "",
|
||||
"Dump All Bins": "",
|
||||
"Dump XCI": "",
|
||||
"Dump Card ID Set": "",
|
||||
"Dump Card UID": "",
|
||||
"Dump Certificate": "",
|
||||
"Dump Initial Data": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"microSD card (/dumps/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Success": "",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Impostazioni Themezer",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Scaricamento json",
|
||||
"Select asset to download for ": "",
|
||||
"Failed to download json": "",
|
||||
"Failed to download app!": "",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"Failed to install, press B to exit...": "",
|
||||
"Install success!": "",
|
||||
"Install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "",
|
||||
"No GameCard inserted": "",
|
||||
"GameCard is already trimmed!": "",
|
||||
"WARNING: GameCard is already trimmed!": "",
|
||||
"Continue": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "Immagine negativa",
|
||||
"Format": "Formato",
|
||||
"Trimming Format": "Formato di ritaglio",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Filtro luce esterno",
|
||||
"Load Default": "Carica predefinito",
|
||||
|
||||
"Web": "",
|
||||
"Select URL": "",
|
||||
"Enter custom URL": "",
|
||||
"Enter URL": "",
|
||||
|
||||
"Advanced": "Avanzato",
|
||||
"Advanced Options": "Opzioni avanzate",
|
||||
"Logging": "Logging",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "hbmenu ripristinato",
|
||||
"Restart Sphaira?": "Vuoi riavviare Sphaira?",
|
||||
"Press OK to restart Sphaira": "Premi OK per riavviare Sphaira",
|
||||
"Boost CPU during transfer": "",
|
||||
"Text scroll speed": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Set left-side menu": "",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "Installa posizione",
|
||||
"System memory": "Memoria di sistema",
|
||||
"microSD card": "Scheda microSD",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Updating ncm database": "",
|
||||
"Pushing application record": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstar": "Rimuovi dai preferiti",
|
||||
"Star": "Aggiungi ai preferiti",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Copia",
|
||||
"Copying ": "Copio",
|
||||
"Paste": "Incolla",
|
||||
"Paste ": "Incolla ",
|
||||
" file(s)?": "(i)file?",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "Incollo",
|
||||
"Pasting": "Incollo",
|
||||
"Rename": "Rinomina",
|
||||
"Set New File Name": "Imposta nuovo nome",
|
||||
"Failed to delete directory": "",
|
||||
"Failed to delete file": "",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Crea cartella",
|
||||
"Set Folder Name": "Imposta nome",
|
||||
"Creating ": "Creazione",
|
||||
"View as text (unfinished)": "Visualizza come testo (non finito)",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Visualizza come testo (non finito)",
|
||||
"Hash": "",
|
||||
"Hash Options": "",
|
||||
"Hashing": "",
|
||||
"Failed to hash file...": "",
|
||||
"Ignore read only": "Ignora read only",
|
||||
"Mount": "Monta",
|
||||
"Sd": "SD",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "Lancia",
|
||||
"Launch option for: ": "Lancia opzione per",
|
||||
"Select launcher for: ": "Scegli launcher per",
|
||||
"Close FileBrowser?": "",
|
||||
|
||||
"Sort By": "Ordina per",
|
||||
"Sort Options": "Opzioni filtro",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "Ricerca",
|
||||
|
||||
"Options": "Opzioni",
|
||||
"Split": "",
|
||||
"OK": "OK",
|
||||
"Back": "Indietro",
|
||||
"Select": "Seleziona",
|
||||
"Open": "Apri",
|
||||
"Close": "",
|
||||
"Launch": "Lancia",
|
||||
"Restart": "Riavvia",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "Rimuovi dai preferiti",
|
||||
"Star": "Aggiungi ai preferiti",
|
||||
"Yes": "Sì",
|
||||
"No": "No",
|
||||
"On": "",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Rimuovi",
|
||||
"Completely remove ": "Elimina definitivamente",
|
||||
"Removing ": "Rimozione",
|
||||
"Removed ": "Rimosso",
|
||||
"Uninstalling ": "Disinstallazione",
|
||||
"Removed ": "Rimosso",
|
||||
|
||||
"Download": "Download",
|
||||
"Downloading ": "Scaricando",
|
||||
"Downloaded ": "Scaricato",
|
||||
"Download via the Network options!": "",
|
||||
|
||||
"Update": "Aggiorna",
|
||||
"Update avaliable: ": "Aggiornamento disponibile",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "Vuoto!",
|
||||
"Not Ready...": "Non pronto...",
|
||||
"Error loading page!": "Errore nel caricare la pagina!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"[Applet Mode]": "[Appletモード]",
|
||||
"No Internet": "インターネットなし",
|
||||
"Switch-Handheld!": "ハンドヘルド!",
|
||||
"Switch-Docked!": "ドック接続!",
|
||||
"Audio disabled due to suspended game": "ゲームが一時停止状態の場合、オーディオは無効になります",
|
||||
"Switch-Handheld!": "ハンドヘルドになりました!",
|
||||
"Switch-Docked!": "ドックに接続しました!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "警告: ログが有効になったため、Sphairaの速度が遅くなります",
|
||||
"Audio disabled due to suspended game": "ゲームが一時停止状態の場合、BGMは無効になります",
|
||||
"Are you sure you wish to cancel?": "本当に取り消しますか?",
|
||||
"An error occurred": "不具合のお知らせ",
|
||||
"If this message appears repeatedly, please open an issue.": "このメッセージが繰り返し表示される場合は、問題を開いてください",
|
||||
"If this message appears repeatedly, please open an issue.": "このメッセージが繰り返し表示される場合、問題を開いてください",
|
||||
|
||||
"Menu Options": "メニュー設定",
|
||||
"Menu": "メニュー",
|
||||
@@ -24,9 +25,11 @@
|
||||
"Ftp": "FTP",
|
||||
"Mtp": "MTP",
|
||||
"Nxlink": "Nxlink",
|
||||
"Nxlink Connected": "Nxlink 接続",
|
||||
"Nxlink Upload": "Nxlink アップロード",
|
||||
"Nxlink Finished": "Nxlink 終了",
|
||||
"Nxlink Connected": "Nxlinkに接続しました",
|
||||
"Nxlink Upload": "Nxlinkにアップロードされました",
|
||||
"Nxlink Finished": "Nxlinkを終了します",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "HDD書き込み保護",
|
||||
|
||||
"Language": "言語",
|
||||
"Auto": "自動",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "メタエントリが見つかりませんでした\n",
|
||||
"Updating application record list": "ゲームのレコードを更新しています",
|
||||
"Dump": "吸出し",
|
||||
"Dump options": "吸出し設定",
|
||||
"Dump Options": "吸出し設定",
|
||||
"Select content to dump": "吸出すコンテンツを選択",
|
||||
"Dump All": "全て",
|
||||
"Dump Application": "ゲームのみ",
|
||||
"Dump Patch": "ゲームパッチのみ",
|
||||
"Dump AddOnContent": "DLCのみ",
|
||||
"Dump DataPatch": "DLCパッチのみ",
|
||||
"Created nested folder": "ネストされたフォルダを作成",
|
||||
"Append folder with .xci": ".xci付きフォルダを追加",
|
||||
"Trim XCI": "XCIをトリム",
|
||||
"Label trimmed XCI": "トリム済みXCIのラベルを指定",
|
||||
"Multi-threaded USB transfer": "マルチスレッドのUSB転送",
|
||||
"Dump All Bins": "すべてのBINを吸出す",
|
||||
"Dump XCI": "XCIを吸出す",
|
||||
"Dump Card ID Set": "ゲームカードIDを吸出す",
|
||||
"Dump Card UID": "ゲームカードUIDを吸出す",
|
||||
"Dump Certificate": "証明書を吸出す",
|
||||
"Dump Initial Data": "初期データを吸出す",
|
||||
"Select dump location": "吸出し位置を選択",
|
||||
"microSD card (/dumps/NSP/)": "SDカード (/dumps/NSP/)",
|
||||
"USB transfer (Switch 2 Switch)": "USB転送 (Switch 2 Switch)",
|
||||
"microSD card (/dumps/)": "SDカード (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "USBで転送 (Switch 2 Switch)",
|
||||
"/dev/null (Speed Test)": "/dev/null (Speed Test)",
|
||||
"Dumping": "吸出し中",
|
||||
"Dump successfull!": "吸出し完了!",
|
||||
"Dump failed!": "吸出し失敗!",
|
||||
"Success": "完了",
|
||||
"Delete successfull!": "削除完了!",
|
||||
"Delete failed!": "削除失敗!",
|
||||
"Success": "完了",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer設定",
|
||||
@@ -86,31 +102,32 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "JSONからダウンロード",
|
||||
"Select asset to download for ": "ダウンロードアイテムを選択 ",
|
||||
"Failed to download json": "JSONからのダウンロードに失敗しました!",
|
||||
"Failed to download app!": "アプリのダウンロードに失敗しました!",
|
||||
|
||||
"FTP Install": "FTPでインストール",
|
||||
"FTP Install (EXPERIMENTAL)": "FTPでインストール(実験機能)",
|
||||
"Connection Type: WiFi | Strength: ": "接続: WiFi | 強度: ",
|
||||
"Connection Type: Ethernet": "接続: イーサネット",
|
||||
"Connection Type: None": "接続: なし",
|
||||
"Host:": "ホースと:",
|
||||
"Port:": "Port:",
|
||||
"Host:": "ホスト:",
|
||||
"Port:": "ポート:",
|
||||
"Username:": "ユーザー名:",
|
||||
"Password:": "暗証番号:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "WiFi暗証番号:",
|
||||
"Failed to install via FTP, press B to exit...": "FTP経由でインストールできませんでした、を押して終了します",
|
||||
"Ftp install success!": "FTPインストール完了!",
|
||||
"Ftp install failed!": "FTPインストール失敗!",
|
||||
"Failed to install, press B to exit...": "FTP経由でインストールできませんでした、を押して終了します",
|
||||
"Install success!": "FTPインストール完了!",
|
||||
"Install failed!": "FTPインストール失敗!",
|
||||
"USB Install": "USBインストール",
|
||||
"USB": "USBインストール",
|
||||
"Connected, waiting for file list...": "接続されました、ファイル リスト待機中",
|
||||
"Connected, starting transfer...": "接続されました、転送開始",
|
||||
"Connected, waiting for file list...": "接続されました、ファイルリストを待っています",
|
||||
"Connected, starting transfer...": "接続されました、転送を開始します",
|
||||
"Failed to init usb, press B to exit...": "USB接続できませんでした、を押して終了します",
|
||||
"Waiting for connection...": "接続待機中",
|
||||
"Transferring data...": "データ転送中",
|
||||
"USB connected, sending file list": "接続されました、ファイルリスト送信中",
|
||||
"Sent file list, waiting for command...": "ファイルリストを送信しました、入力待機中",
|
||||
"waiting for usb connection...": "USB接続待機中",
|
||||
"Transferring data...": "データを転送しています",
|
||||
"USB connected, sending file list": "接続されました、ファイルリスト送信します",
|
||||
"Sent file list, waiting for command...": "ファイルリストを送信しました、入力を待っています",
|
||||
"waiting for usb connection...": "USBの接続を待っています",
|
||||
"Disable MTP for usb install": "USBインストールのため、MTPを無効にします",
|
||||
"Re-enabled MTP": "MTPに再接続します",
|
||||
"Installed via usb": "USBインストールに成功しました",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "ゲームカード",
|
||||
"System memory %.1f GB": "本体保存メモリー %.1f GB",
|
||||
"microSD card %.1f GB": "SDカード %.1f GB",
|
||||
"Nand Install": "本体保存メモリーにインストール",
|
||||
"SD Card Install": "SDカードにインストール",
|
||||
"Exit": "もどる",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "インストールが無効化されています\nインストール設定で有効にしてください",
|
||||
"No GameCard inserted": "ゲームカードが挿入されていません",
|
||||
"GameCard is already trimmed!": "ゲームカードがすでにトリムされています!",
|
||||
"WARNING: GameCard is already trimmed!": "警告: ゲームカードがすでにトリムされています!",
|
||||
"Continue": "つづく",
|
||||
"Gc install success!": "ゲームカードインストール完了!",
|
||||
"Gc install failed!": "ゲームカードインストール失敗!",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "ネガティブなイメージ",
|
||||
"Format": "解像度",
|
||||
"Trimming Format": "トリミングされた解像度",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "外光フィルター",
|
||||
"Load Default": "基本設定に戻す",
|
||||
|
||||
"Web": "ウェブサイト",
|
||||
"Select URL": "URLを選択してください",
|
||||
"Enter custom URL": "URLを直接入力",
|
||||
"Enter URL": "URLを記入してください",
|
||||
|
||||
"Advanced": "高度な",
|
||||
"Advanced Options": "高度設定",
|
||||
"Logging": "ログの取得",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "hbmenuに復元されました",
|
||||
"Restart Sphaira?": "Sphairaを再起動しますか?",
|
||||
"Press OK to restart Sphaira": "確認ボタンを押してSphairaを再起動",
|
||||
"Boost CPU during transfer": "転送する間CPUを加速",
|
||||
"Text scroll speed": "流れる文字の速さ",
|
||||
"Slow": "遅い",
|
||||
"Slow": "遅く",
|
||||
"Normal": "普通",
|
||||
"Fast": "速い",
|
||||
"Fast": "速く",
|
||||
"Set left-side menu": "左側メニュー設定",
|
||||
"Set right-side menu": "右側メニュー設定",
|
||||
"Install options": "インストール設定",
|
||||
"Install Options": "インストール設定",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "インストール経路",
|
||||
"System memory": "本体保存メモリー",
|
||||
"microSD card": "SDカード",
|
||||
"Boost CPU clock": "CPUクロックをブースト",
|
||||
"Allow downgrade": "ダウングレード許可",
|
||||
"Skip if already installed": "既にインストールされている場合はスキップします",
|
||||
"Ticket only": "チケットのみ設置",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "コントロール作成中",
|
||||
"Creating Meta": "メター作成中",
|
||||
"Writing Nca": "Nca書き取り中",
|
||||
"Updating ncm databse": "ncmのDBをアップデート中",
|
||||
"Updating ncm database": "ncmのDBをアップデート中",
|
||||
"Pushing application record": "アプリの記録をプッシュ中",
|
||||
"Failed to install forwarder": "Forwarderのインストール失敗",
|
||||
"Unstar": "お気に入り解除",
|
||||
"Star": "お気に入り",
|
||||
"Unstarred ": "お気に入り解除: ",
|
||||
"Starred ": "お気に入りに登録: ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "古いForwarderを削除できませんでした、手動で削除してください!",
|
||||
@@ -247,8 +270,8 @@
|
||||
|
||||
"FileBrowser": "ファイルブラウザ",
|
||||
"Files": "ファイル",
|
||||
"%zd files": "%zd個のファイル",
|
||||
"%zd dirs": "%zd個のフォルダー",
|
||||
"%zd files": "%zd ファイル",
|
||||
"%zd dirs": "%zd フォルダー",
|
||||
"File Options": "ファイル設定",
|
||||
"Show Hidden": "非表示ファイルを表示",
|
||||
"Folders First": "フォルダーを優先",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "コピー",
|
||||
"Copying ": "コピー中 ",
|
||||
"Paste": "ペースト",
|
||||
"Paste ": " ",
|
||||
" file(s)?": "個のファイルをペーストしますか?",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "ペースト中 ",
|
||||
"Pasting": "ペースト中",
|
||||
"Rename": "名前の変更",
|
||||
"Set New File Name": "新しい名前を入力",
|
||||
"Failed to delete directory": "フォルダーを削除できませんでした",
|
||||
"Failed to delete file": "ファイルを削除できませんでした",
|
||||
"Extract zip": "ZIPファイルを解凍",
|
||||
"Extract Options": "解凍設定",
|
||||
"Extract here": "ここに解凍",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "フォルダーの作成",
|
||||
"Set Folder Name": "名前を入力",
|
||||
"Creating ": "作成中 ",
|
||||
"View as text (unfinished)": "テキストとして表示 (未完成)",
|
||||
"Upload": "アップロード",
|
||||
"Select upload location": "アップロードの位置を設定",
|
||||
"No upload locations set!": "アップロードの位置が設定されていません",
|
||||
"Uploading": "アップロード中",
|
||||
"Upload successfull!": "アップロード完了!",
|
||||
"Upload failed!": "アップロード失敗!",
|
||||
"View as text (unfinished)": "テキストとして表示 (未完成)",
|
||||
"Hash": "ハッシュ",
|
||||
"Hash Options": "ハッシュ設定",
|
||||
"Hashing": "ハッシュ化中",
|
||||
"Failed to hash file...": "ハッシュ化できませんでした",
|
||||
"Ignore read only": "読み取り専用を無視する",
|
||||
"Mount": "マウント",
|
||||
"Sd": "SDメモリーカード",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "起動しますか",
|
||||
"Launch option for: ": "起動設定: ",
|
||||
"Select launcher for: ": "起動ランチャーを選ぶ: ",
|
||||
"Close FileBrowser?": "ファイルブラウザを閉じますか?",
|
||||
|
||||
"Sort By": "並べ替え",
|
||||
"Sort Options": "並べ替え設定",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "検索",
|
||||
|
||||
"Options": "設定",
|
||||
"Split": "スクリーン分割",
|
||||
"OK": "確認",
|
||||
"Back": "戻る",
|
||||
"Select": "選択",
|
||||
"Open": "開く",
|
||||
"Close": "閉じる",
|
||||
"Launch": "起動",
|
||||
"Restart": "再起動",
|
||||
"Next": "次へ",
|
||||
"Prev": "前へ",
|
||||
"Unstar": "お気に入り解除",
|
||||
"Star": "お気に入り",
|
||||
"Yes": "はい",
|
||||
"No": "いいえ",
|
||||
"On": "オン",
|
||||
@@ -367,18 +396,19 @@
|
||||
"Remove": "除去",
|
||||
"Completely remove ": "除去しますか ",
|
||||
"Removing ": "除去中 ",
|
||||
"Removed ": "除去完了 ",
|
||||
"Uninstalling ": "アンインストール中 ",
|
||||
"Removed ": "除去完了 ",
|
||||
|
||||
"Download": "ダウンロード",
|
||||
"Downloading ": "ダウンロード中 ",
|
||||
"Downloaded ": "ダウンロード完了 ",
|
||||
"Download via the Network options!": "ネットワーク設定からダウンロードしました!",
|
||||
|
||||
"Update": "アップデート",
|
||||
"Update avaliable: ": "アップデート可能: ",
|
||||
"Download update: ": "アップデートをダウンロード: ",
|
||||
"Updated to ": "アップデート: ",
|
||||
"Failed to download update": "アップデートのダウンロード失敗",
|
||||
"Failed to download update": "アップデートのダウンロードに失敗しました",
|
||||
|
||||
"%zu hours %zu minutes remaining": "残り %zu 時間 %zu 分",
|
||||
"%zu minutes %zu seconds remaining": "残り %zu 分",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "何も見つかりません",
|
||||
"Not Ready...": "準備ができていません",
|
||||
"Error loading page!": "ページのロードエラー"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "인터넷 연결 없음",
|
||||
"Switch-Handheld!": "휴대모드로 전환됨!",
|
||||
"Switch-Docked!": "독 모드로 전환됨!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "경고: 로깅 활성화, 앱이 느려집니다!",
|
||||
"Audio disabled due to suspended game": "게임 실행 중에는 BGM이 비활성화 됩니다",
|
||||
"Are you sure you wish to cancel?": "정말 취소할까요?",
|
||||
"An error occurred": "오류가 발생했습니다!",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink 연결됨",
|
||||
"Nxlink Upload": "Nxlink 업로드",
|
||||
"Nxlink Finished": "Nxlink 종료됨",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "HDD 쓰기 방지",
|
||||
|
||||
"Language": "언어",
|
||||
"Auto": "자동",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "메타 항목을 찾을 수 없습니다...\n",
|
||||
"Updating application record list": "앱 기록 업데이트 중",
|
||||
"Dump": "덤프",
|
||||
"Select content to dump": "덤프 옵션",
|
||||
"Dump options": "덤프 옵션",
|
||||
"Dump Options": "덤프 옵션",
|
||||
"Select content to dump": "덤프 콘텐츠 선택",
|
||||
"Dump All": "모든 콘텐츠",
|
||||
"Dump Application": "게임",
|
||||
"Dump Patch": "게임 패치",
|
||||
"Dump AddOnContent": "DLC",
|
||||
"Dump DataPatch": "DLC 패치",
|
||||
"Created nested folder": "중첩 폴더 생성",
|
||||
"Append folder with .xci": ".xci 파일이 포함된 폴더 추가",
|
||||
"Trim XCI": "XCI 트림",
|
||||
"Label trimmed XCI": "트림된 XCI의 라벨 지정",
|
||||
"Multi-threaded USB transfer": "멀티스레드 USB 전송",
|
||||
"Dump All Bins": "모든 바이너리 덤프",
|
||||
"Dump XCI": "XCI 덤프",
|
||||
"Dump Card ID Set": "카트리지 ID 덤프",
|
||||
"Dump Card UID": "카트리지 UID 덤프",
|
||||
"Dump Certificate": "서명 덤프",
|
||||
"Dump Initial Data": "초기화 데이터 덤프",
|
||||
"Select dump location": "덤프 위치",
|
||||
"microSD card (/dumps/NSP/)": "SD 카드 (sdmc:/dumps/NSP/)",
|
||||
"microSD card (/dumps/)": "SD 카드 (sdmc:/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "USB 전송 (Switch 2 Switch)",
|
||||
"/dev/null (Speed Test)": "/dev/null (Speed Test)",
|
||||
"/dev/null (Speed Test)": "/dev/null (벤치마크)",
|
||||
"Dumping": "덤프 중",
|
||||
"Dump successfull!": "덤프 완료!",
|
||||
"Dump failed!": "덤프 실패!",
|
||||
"Success": "완료!",
|
||||
"Delete successfull!": "삭제 완료!",
|
||||
"Delete failed!": "삭제 실패!",
|
||||
"Success": "완료!",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer 옵션",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "JSON에서 다운로드",
|
||||
"Select asset to download for ": "다운로드 아이템: ",
|
||||
"Failed to download json": "JSON에서 다운로드 실패!",
|
||||
"Failed to download app!": "앱 다운로드 실패!",
|
||||
|
||||
"FTP Install": "FTP 설치",
|
||||
"FTP Install (EXPERIMENTAL)": "FTP 설치 (실험실 기능)",
|
||||
"Connection Type: WiFi | Strength: ": "상태: WiFi | 신호 세기: ",
|
||||
"Connection Type: Ethernet": "상태: 이더넷",
|
||||
"Connection Type: None": "상태: 연결 없음",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "비밀번호:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "WiFi 암호:",
|
||||
"Failed to install via FTP, press B to exit...": "FTP 설치 실패함, 종료하려면 버튼을 입력하세요...",
|
||||
"Ftp install success!": "FTP 설치 완료!",
|
||||
"Ftp install failed!": "FTP 설치 실패!",
|
||||
"Failed to install, press B to exit...": "FTP 설치 실패함, 종료하려면 버튼을 입력하세요...",
|
||||
"Install success!": "FTP 설치 완료!",
|
||||
"Install failed!": "FTP 설치 실패!",
|
||||
"USB Install": "USB 설치",
|
||||
"USB": "USB 설치",
|
||||
"Connected, waiting for file list...": "연결됨, 파일 목록 대기 중...",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "카트리지",
|
||||
"System memory %.1f GB": "본체 저장 메모리 %.1f GB",
|
||||
"microSD card %.1f GB": "SD 카드 %.1f GB",
|
||||
"Nand Install": "본체 저장 메모리에 설치",
|
||||
"SD Card Install": "SD 카드에 설치",
|
||||
"Exit": "나가기",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "설치가 비활성화 되었습니다\n설치 옵션에서 활성화해주세요",
|
||||
"No GameCard inserted": "카트리지가 없습니다",
|
||||
"GameCard is already trimmed!": "카트리지를 이미 트림했습니다",
|
||||
"WARNING: GameCard is already trimmed!": "경고: 카트리지를 이미 트림했습니다",
|
||||
"Continue": "계속",
|
||||
"Gc install success!": "카트리지 설치 완료!",
|
||||
"Gc install failed!": "카트리지 설치 실패!",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "반전",
|
||||
"Format": "해상도",
|
||||
"Trimming Format": "트리밍 해상도",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "외부 조명 필터",
|
||||
"Load Default": "기본값으로 설정",
|
||||
|
||||
"Web": "웹 브라우저",
|
||||
"Select URL": "URL 주소 선택",
|
||||
"Enter custom URL": "직접 지정",
|
||||
"Enter URL": "URL을 기입하세요",
|
||||
|
||||
"Advanced": "고급",
|
||||
"Advanced Options": "고급 옵션",
|
||||
"Logging": "로깅",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "hbmenu 복원됨",
|
||||
"Restart Sphaira?": "Sphaira를 재시작할까요?",
|
||||
"Press OK to restart Sphaira": "확인 버튼 입력하여 Sphaira 재시작",
|
||||
"Boost CPU during transfer": "전송시 CPU 부스트",
|
||||
"Text scroll speed": "긴 텍스트 표시 속도",
|
||||
"Slow": "천천히",
|
||||
"Normal": "보통",
|
||||
"Fast": "빠르게",
|
||||
"Set left-side menu": "좌측 메뉴 설정",
|
||||
"Set right-side menu": "우측 메뉴 설정",
|
||||
"Install options": "설치 옵션",
|
||||
"Install Options": "설치 옵션",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "설치 위치",
|
||||
"System memory": "본체 저장 메모리",
|
||||
"microSD card": "SD 카드",
|
||||
"Boost CPU clock": "CPU 클럭 향상",
|
||||
"Allow downgrade": "다운그레이드 허용",
|
||||
"Skip if already installed": "설치된 항목 건너뛰기",
|
||||
"Ticket only": "티켓만 설치",
|
||||
@@ -217,14 +238,16 @@
|
||||
"Hide Sphaira": "Sphaira 숨기기",
|
||||
"Install Forwarder": "바로가기 설치",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "경고: 시스낸드에 설치 시, 밴 위험이 있습니다!",
|
||||
"Installing Forwarder": "바로가기 설치",
|
||||
"Installing Forwarder": "바로가기 설치 중",
|
||||
"Creating Program": "프로그램 생성",
|
||||
"Creating Control": "컨트롤 생성",
|
||||
"Creating Meta": "메타 생성",
|
||||
"Writing Nca": "Nca 쓰기",
|
||||
"Updating ncm databse": "Ncm 데이터베이스 업데이트",
|
||||
"Updating ncm database": "Ncm 데이터베이스 업데이트",
|
||||
"Pushing application record": "응용 프로그램 기록 푸싱",
|
||||
"Failed to install forwarder": "바로가기 설치 실패함",
|
||||
"Unstar": "즐겨찾기 해제",
|
||||
"Star": "즐겨찾기",
|
||||
"Unstarred ": "즐겨찾기 해제: ",
|
||||
"Starred ": "즐겨찾기 등록: ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "바로가기 제거 실패함, 직접 제거해주세요!",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "복사",
|
||||
"Copying ": "복사 중 ",
|
||||
"Paste": "붙여넣기",
|
||||
"Paste ": " ",
|
||||
" file(s)?": "개 항목을 붙여넣을까요?",
|
||||
"Paste file(s)?": "붙여넣을까요?",
|
||||
"Pasting ": "붙여넣는 중 ",
|
||||
"Pasting": "붙여넣기",
|
||||
"Pasting": "붙여넣는 중",
|
||||
"Rename": "이름 바꾸기",
|
||||
"Set New File Name": "새 파일명 입력",
|
||||
"Failed to delete directory": "폴더 삭제 실패",
|
||||
"Failed to delete file": "파일 삭제 실패",
|
||||
"Extract zip": "압축 해제",
|
||||
"Extract Options": "압축 해제 옵션",
|
||||
"Extract here": "여기에 풀기",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "새 폴더",
|
||||
"Set Folder Name": "폴더명 입력",
|
||||
"Creating ": "생성 중 ",
|
||||
"View as text (unfinished)": "텍스트로 보기 (미완성)",
|
||||
"Upload": "업로드",
|
||||
"Select upload location": "업로드 위치 선택",
|
||||
"No upload locations set!": "업로드 위치가 설정되지 않았습니다!",
|
||||
"Uploading": "업로드 중 ",
|
||||
"Upload successfull!": "업로드 완료!",
|
||||
"Upload failed!": "업로드 실패!",
|
||||
"View as text (unfinished)": "텍스트로 보기 (미완성)",
|
||||
"Hash": "해시",
|
||||
"Hash Options": "해시 옵션",
|
||||
"Hashing": "해시화중",
|
||||
"Failed to hash file...": "해시화에 실패했습니다...",
|
||||
"Ignore read only": "읽기 전용 설정 무시",
|
||||
"Mount": "마운트",
|
||||
"Sd": "SD 카드",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "실행할까요 ",
|
||||
"Launch option for: ": "실행 옵션: ",
|
||||
"Select launcher for: ": "실행 런처: ",
|
||||
"Close FileBrowser?": "파일 탐색창을 닫을까요?",
|
||||
|
||||
"Sort By": "정렬",
|
||||
"Sort Options": "정렬 옵션",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "검색",
|
||||
|
||||
"Options": "설정",
|
||||
"Split": "화면 분할",
|
||||
"OK": "확인",
|
||||
"Back": "뒤로",
|
||||
"Select": "선택",
|
||||
"Open": "열기",
|
||||
"Close": "닫기",
|
||||
"Launch": "실행",
|
||||
"Restart": "재시작",
|
||||
"Next": "다음",
|
||||
"Prev": "이전",
|
||||
"Unstar": "즐겨찾기 해제",
|
||||
"Star": "즐겨찾기",
|
||||
"Yes": "예",
|
||||
"No": "아니요",
|
||||
"On": "켬",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "제거",
|
||||
"Completely remove ": "정말 삭제할까요 ",
|
||||
"Removing ": "제거 중 ",
|
||||
"Uninstalling ": "설치 제거중 ",
|
||||
"Removed ": "제거됨: ",
|
||||
"Uninstalling ": "설치 제거됨: ",
|
||||
|
||||
"Download": "다운로드",
|
||||
"Downloading ": "다운로드 중 ",
|
||||
"Downloaded ": "다운로드 완료: ",
|
||||
"Download via the Network options!": "네트워크 옵션에서 다운로드했습니다!",
|
||||
|
||||
"Update": "업데이트",
|
||||
"Update avaliable: ": "업데이트 가능: ",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "찾을 수 없습니다!",
|
||||
"Not Ready...": "준비되지 않음...",
|
||||
"Error loading page!": "페이지 로딩 오류!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "Geen internet",
|
||||
"Switch-Handheld!": "",
|
||||
"Switch-Docked!": "",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"Are you sure you wish to cancel?": "",
|
||||
"An error occurred": "",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "",
|
||||
"Nxlink Upload": "",
|
||||
"Nxlink Finished": "",
|
||||
"Hdd": "",
|
||||
"Hdd write protect": "",
|
||||
|
||||
"Language": "Taal",
|
||||
"Auto": "",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Dump options": "",
|
||||
"Dump Options": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Created nested folder": "",
|
||||
"Append folder with .xci": "",
|
||||
"Trim XCI": "",
|
||||
"Label trimmed XCI": "",
|
||||
"Multi-threaded USB transfer": "",
|
||||
"Dump All Bins": "",
|
||||
"Dump XCI": "",
|
||||
"Dump Card ID Set": "",
|
||||
"Dump Card UID": "",
|
||||
"Dump Certificate": "",
|
||||
"Dump Initial Data": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"microSD card (/dumps/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Success": "",
|
||||
|
||||
"Themezer": "Themamaker",
|
||||
"Themezer Options": "",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "",
|
||||
"Downloading json": "",
|
||||
"Select asset to download for ": "",
|
||||
"Failed to download json": "",
|
||||
"Failed to download app!": "",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"Failed to install, press B to exit...": "",
|
||||
"Install success!": "",
|
||||
"Install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "",
|
||||
"No GameCard inserted": "",
|
||||
"GameCard is already trimmed!": "",
|
||||
"WARNING: GameCard is already trimmed!": "",
|
||||
"Continue": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "Negatief beeld",
|
||||
"Format": "Formaat",
|
||||
"Trimming Format": "Trimformaat",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Extern lichtfilter",
|
||||
"Load Default": "Standaard laden",
|
||||
|
||||
"Web": "",
|
||||
"Select URL": "",
|
||||
"Enter custom URL": "",
|
||||
"Enter URL": "",
|
||||
|
||||
"Advanced": "Geavanceerd",
|
||||
"Advanced Options": "Bestand maken",
|
||||
"Logging": "Loggen",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "",
|
||||
"Restart Sphaira?": "",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Boost CPU during transfer": "",
|
||||
"Text scroll speed": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Set left-side menu": "",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "",
|
||||
"System memory": "",
|
||||
"microSD card": "",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "",
|
||||
"Creating Meta": "",
|
||||
"Writing Nca": "",
|
||||
"Updating ncm databse": "",
|
||||
"Updating ncm database": "",
|
||||
"Pushing application record": "",
|
||||
"Failed to install forwarder": "",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"Unstarred ": "",
|
||||
"Starred ": "",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Kopiëren",
|
||||
"Copying ": "",
|
||||
"Paste": "",
|
||||
"Paste ": "",
|
||||
" file(s)?": "",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "",
|
||||
"Pasting": "",
|
||||
"Rename": "Hernoemen",
|
||||
"Set New File Name": "",
|
||||
"Failed to delete directory": "",
|
||||
"Failed to delete file": "",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Map maken",
|
||||
"Set Folder Name": "",
|
||||
"Creating ": "",
|
||||
"View as text (unfinished)": "Bekijk als tekst (onvoltooid)",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Bekijk als tekst (onvoltooid)",
|
||||
"Hash": "",
|
||||
"Hash Options": "",
|
||||
"Hashing": "",
|
||||
"Failed to hash file...": "",
|
||||
"Ignore read only": "",
|
||||
"Mount": "",
|
||||
"Sd": "",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "",
|
||||
"Launch option for: ": "",
|
||||
"Select launcher for: ": "",
|
||||
"Close FileBrowser?": "",
|
||||
|
||||
"Sort By": "Sorteer op",
|
||||
"Sort Options": "Sorteeropties",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "Zoekopdracht",
|
||||
|
||||
"Options": "Opties",
|
||||
"Split": "",
|
||||
"OK": "",
|
||||
"Back": "Terug",
|
||||
"Select": "",
|
||||
"Open": "Open",
|
||||
"Close": "",
|
||||
"Launch": "Launch",
|
||||
"Restart": "",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "",
|
||||
"Star": "",
|
||||
"Yes": "Ja",
|
||||
"No": "Nee",
|
||||
"On": "",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "",
|
||||
"Completely remove ": "",
|
||||
"Removing ": "",
|
||||
"Removed ": "",
|
||||
"Uninstalling ": "",
|
||||
"Removed ": "",
|
||||
|
||||
"Download": "Downloaden",
|
||||
"Downloading ": "",
|
||||
"Downloaded ": "",
|
||||
"Download via the Network options!": "",
|
||||
|
||||
"Update": "",
|
||||
"Update avaliable: ": "",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "",
|
||||
"Not Ready...": "",
|
||||
"Error loading page!": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
"No Internet": "Sem internet",
|
||||
"Switch-Handheld!": "Switch-Portátil",
|
||||
"Switch-Docked!": "Switch-Docado",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "AVISO: O registro de depuração está habilitado, o sphaira ficará lento!",
|
||||
"Audio disabled due to suspended game": "Áudio desativado devido ao software suspenso.",
|
||||
"Are you sure you wish to cancel?": "Tem certeza de que quer cancelar?",
|
||||
"An error occurred": "Ocorreu um erro.",
|
||||
"If this message appears repeatedly, please open an issue.": "Se esta mensagem aparecer repetidamente, abra um issue.",
|
||||
|
||||
"Menu Options": "Opções do menu",
|
||||
"Menu Options": "Menu",
|
||||
"Menu": "Menu",
|
||||
"Theme": "Tema",
|
||||
"Theme Options": "Opções de tema",
|
||||
"Theme Options": "Tema",
|
||||
"Select Theme": "Estilo",
|
||||
"Music": "Música",
|
||||
"12 Hour Time": "Relógio de 12 horas",
|
||||
@@ -20,7 +21,7 @@
|
||||
"Overwrite current default music?": "Substituir a música padrão atual?",
|
||||
|
||||
"Network": "Rede",
|
||||
"Network Options": "Opções de rede",
|
||||
"Network Options": "Rede",
|
||||
"Ftp": "Servidor FTP",
|
||||
"Mtp": "Escuta MTP",
|
||||
"Nxlink": "Nxlink",
|
||||
@@ -48,23 +49,37 @@
|
||||
"Ukrainian": "Українська",
|
||||
|
||||
"Misc": "Diversos",
|
||||
"Misc Options": "Opções diversas",
|
||||
"Misc Options": "Diversos",
|
||||
|
||||
"Games": "Softwares",
|
||||
"Game Options": "Opções de software",
|
||||
"Game Options": "Opções do software",
|
||||
"Hide forwarders": "Ocultar atalhos forwarder",
|
||||
"Launch random game": "Iniciar um software aleatório",
|
||||
"List meta records": "Registro de dados",
|
||||
"List meta records": "Lista de registros",
|
||||
"Entries": "Entradas",
|
||||
"Failed to list application meta entries": "Falha ao listar as meta entradas do aplicativo.",
|
||||
"Failed to list application meta entries": "Falha ao listar as meta entradas do software.",
|
||||
"No meta entries found...\n": "Nenhuma entrada de registros encontrada...\n",
|
||||
"Updating application record list": "Atualizando a lista de registros do software...",
|
||||
"Dump": "Exportar dados",
|
||||
"Select content to dump": "Exportação de dados",
|
||||
"Dump": "Exportar software",
|
||||
"Dump options": "Opções de exportação",
|
||||
"Dump Options": "Opções de exportação",
|
||||
"Select content to dump": "Exportação de software",
|
||||
"Dump All": "Exportar tudo",
|
||||
"Dump Application": "Exportar software base",
|
||||
"Dump Patch": "Exportar atualização",
|
||||
"Dump AddOnContent": "Exportar DLCs",
|
||||
"Dump DataPatch": "Exportar atualização de DLCs",
|
||||
"Created nested folder": "Criar pastas aninhadas",
|
||||
"Append folder with .xci": "Acrescentar .xci à pasta",
|
||||
"Trim XCI": "Aparar arquivos .xci",
|
||||
"Label trimmed XCI": "Rotular .xci aparados",
|
||||
"Multi-threaded USB transfer": "Transferências em multithread",
|
||||
"Dump All Bins": "Exportar todos os binários",
|
||||
"Dump XCI": "Exportar .xci",
|
||||
"Dump Card ID Set": "Exportar conjunto de IDs",
|
||||
"Dump Card UID": "Exportar UID",
|
||||
"Dump Certificate": "Exportar certificado",
|
||||
"Dump Initial Data": "Exportar dados iniciais",
|
||||
"Select dump location": "Selecione o local de exportação",
|
||||
"microSD card (/dumps/)": "Cartão microSD (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "Transferência via USB (Switch 2 Switch)",
|
||||
@@ -72,27 +87,59 @@
|
||||
"Dumping": "Exportando...",
|
||||
"Dump successfull!": "Exportação concluída.",
|
||||
"Dump failed!": "Exportação falhou.",
|
||||
"Success": "Concluído.",
|
||||
"Delete successfull!": "Remoção concluída.",
|
||||
"Delete failed!": "Remoção falhou.",
|
||||
"Success": "Concluído.",
|
||||
"Refresh": "Recarregar",
|
||||
"Create contents folder": "Criar pasta de conteúdo",
|
||||
"Create save": "Criar dados salvo",
|
||||
"Title cache": "Usar cache de títulos",
|
||||
"Delete title cache": "Apagar cache de títulos",
|
||||
|
||||
"Saves": "Dados salvos",
|
||||
"Save Options": "Opções de dados salvos",
|
||||
"Account": "Usuário",
|
||||
"Data Type": "Tipo de dados",
|
||||
"System": "Sistema",
|
||||
"BCAT": "BCAT",
|
||||
"Device": "Dispositivo",
|
||||
"Temporary": "Temporário",
|
||||
"Cache": "Cache",
|
||||
"System BCAT": "BCAT (Sistema)",
|
||||
"Backup": "Backup",
|
||||
"Auto backup": "Backup automático",
|
||||
"Auto backup on restore": "Fazer backup ao restaurar",
|
||||
"Compress backup": "Comprimir backups",
|
||||
"Are you sure you want to backup save(s)?": "Tem certeza de que quer fazer backup dos dados salvo?",
|
||||
"No saves found in ": "Nenhum dado salvo encontrado em ",
|
||||
"Backed up to ": "Backup salvo em ",
|
||||
"Backup successfull!": "Backup concluído.",
|
||||
"Backup failed!": "Backup falhou.",
|
||||
"Select backup location": "Selecione o local de backup",
|
||||
"Select restore location": "Selecione o local de restauração",
|
||||
"Restore save for: ": "Restaurar backup de dados salvo para: ",
|
||||
"Are you sure you want to restore ": "Tem certeza de que quer restaurar os dados salvo de ",
|
||||
"Restore successfull!": "Restauração concluída.",
|
||||
"Restore failed!": "Restauração falhou.",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Opções do Themezer",
|
||||
"Nsfw": "Temas 18+ (NSFW)",
|
||||
"Page": "Ir para uma página específica",
|
||||
"Page": "Página",
|
||||
"Page %zu / %zu": "Página %zu / %zu",
|
||||
"Enter Page Number": "Número da página",
|
||||
"Enter Page Number": "Digite o № 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 de ",
|
||||
"Failed to download json": "Falha ao baixar json.",
|
||||
"Failed to download app!": "falha ao baixar aplicativo.",
|
||||
|
||||
"FTP Install": "Instalação via FTP",
|
||||
"FTP Install (EXPERIMENTAL)": "Instalação via FTP (EXPERIMENTAL)",
|
||||
"Connection Type: WiFi | Strength: ": "Conexão por rede Wi-Fi | Intensidade do sinal: ",
|
||||
"Connection Type: Ethernet": "Conexão por cabo (ethernet)",
|
||||
"Connection Type: Ethernet": "Conexão por cabo Ethernet",
|
||||
"Connection Type: None": "Sem conexão",
|
||||
"Host:": "Host:",
|
||||
"Port:": "Porta:",
|
||||
@@ -100,33 +147,53 @@
|
||||
"Password:": "Senha:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "Senha:",
|
||||
"Failed to install via FTP, press B to exit...": "Falha ao instalar via FTP, aperte B para sair.",
|
||||
"Ftp install success!": "Instalação via FTP concluída.",
|
||||
"Ftp install failed!": "Instalação via FTP falhou.",
|
||||
"Failed to install, press B to exit...": "Falha ao instalar, aperte para sair.",
|
||||
"Install success!": "Instalação concluída.",
|
||||
"Install failed!": "Instalação falhou.",
|
||||
"MTP Install": "Instalação via MTP",
|
||||
"State: %s | Speed: %s": "Estado: %s | Velocidade: %s",
|
||||
"Detached": "Desconectado",
|
||||
"Attached": "Conectado",
|
||||
"Powered": "Energia",
|
||||
"Default": "Padrão",
|
||||
"Address": "Endereço",
|
||||
"Configured": "Configurado",
|
||||
"Suspended": "Suspenso",
|
||||
"USB 1.0 Low Speed": "USB 1.0",
|
||||
"USB 1.1 Full Speed": "USB 1.1 (Full Speed)",
|
||||
"USB 2.0 High Speed": "USB 2.0 (High Speed)",
|
||||
"USB 3.0 Super Speed": "USB 3.0 (SuperSpeed)",
|
||||
"Drag'n'Drop (NSP, XCI, NSZ, XCZ) to the install folder": "Arraste o(s) arquivo(s) .nsp/.xci/.nsz/.xcz para a pasta \"Install\".",
|
||||
"Failed to install via MTP, press B to exit...": "Falha ao instalar via MTP, aperte para sair.",
|
||||
"MTP install success!": "Instalação via MTP concluída.",
|
||||
"MTP install failed!": "Instalação via MTP falhou.",
|
||||
"USB Install": "Instalação via USB",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "Conectado, aguardando lista de arquivos...",
|
||||
"Connected, starting transfer...": "Conectado, iniciando transferência...",
|
||||
"Failed to init usb, press B to exit...": "Falha ao instalar via USB,\naperte B para sair.",
|
||||
"Failed to init usb, press B to exit...": "Falha ao inicializar USB,\naperte para sair.",
|
||||
"Waiting for connection...": "Aguardando conexão...",
|
||||
"Transferring data...": "Transferindo dados...",
|
||||
"USB connected, sending file list": "USB conectado, enviando lista de arquivos",
|
||||
"Sent file list, waiting for command...": "Lista de arquivos enviada, aguardando comando...",
|
||||
"waiting for usb connection...": "Aguardando conexão USB...",
|
||||
"Disable MTP for usb install": "Escuta MTP desabilitada temporáriamente.",
|
||||
"Disable MTP for usb install": "Escuta MTP desabilitada temporariamente.",
|
||||
"Re-enabled MTP": "Escuta MTP reabilitada.",
|
||||
"Installed via usb": "Instalado via USB",
|
||||
"Usb install success!": "Instalação via USB concluída.",
|
||||
"Usb install failed!": "Instalação via USB falhou.",
|
||||
"Press B to exit...": "Aperte B para sair.",
|
||||
"Press B to exit...": "Aperte para sair.",
|
||||
"GameCard Install": "Instalação de cartão de jogo",
|
||||
"GameCard": "Cartão de jogo",
|
||||
"GC": "Cartão de jogo",
|
||||
"System memory %.1f GB": "Memória do console %.1f GB",
|
||||
"microSD card %.1f GB": "Cartão microSD %.1f GB",
|
||||
"Nand Install": "Instalar na memória do console",
|
||||
"SD Card Install": "Instalar no cartão microSD",
|
||||
"Exit": "Sair",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Instalação desabilitada.\nHabilite a função através das opções de instalação.",
|
||||
"No GameCard inserted": "Nenhum cartão de jogo inserido.",
|
||||
"GameCard is already trimmed!": "O cartão de jogo já está aparado.",
|
||||
"WARNING: GameCard is already trimmed!": "AVISO: O cartão de jogo já está aparado.",
|
||||
"Continue": "Continuar",
|
||||
"Gc install success!": "Instalação de cartão de jogo concluída.",
|
||||
"Gc install failed!": "Instalação de cartão de jogo falhou.",
|
||||
|
||||
@@ -162,22 +229,23 @@
|
||||
"Negative image": "Imagem negativa",
|
||||
"Format": "Formato",
|
||||
"Trimming Format": "Formato do recorte",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Filtro de luz externa",
|
||||
"Load Default": "Restaurar padrão",
|
||||
|
||||
"Advanced": "Avançados",
|
||||
"Web": "Navegador de internet",
|
||||
"Select URL": "Selecione uma URL",
|
||||
"Enter custom URL": "Digitar URL",
|
||||
"Enter URL": "Digitar URL",
|
||||
|
||||
"Advanced": "Avançado",
|
||||
"Advanced Options": "Opções avançadas",
|
||||
"Logging": "Registro de depuração",
|
||||
"Advanced options": "Opções avançadas",
|
||||
"Logging": "Registros de depuração",
|
||||
"Replace hbmenu on exit": "Substituir hbmenu ao sair",
|
||||
"Restore hbmenu?": "Restaurar hbmenu?",
|
||||
"Restore": "Restaurar",
|
||||
"Failed to find /switch/hbmenu.nro\nUse the Appstore to re-install hbmenu": "Falha ao buscar /switch/hbmenu.nro\nUse a loja (AppStore) para reinstalar o hbmenu.",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Falha ao restaurar o hbmenu, tente baixa-lo novamente.",
|
||||
"Failed to restore hbmenu, please re-download hbmenu": "Falha ao restaurar hbmenu, tente baixa-lo novamente.",
|
||||
"Failed to restore hbmenu, using sphaira instead": "Falha ao restaurar hbmenu, usando sphaira no seu lugar.",
|
||||
"Restored hbmenu, closing sphaira": "hbmenu restaurado, fechando sphaira.",
|
||||
"Restored hbmenu": "hbmenu restaurado.",
|
||||
@@ -192,13 +260,12 @@
|
||||
"Set right-side menu": "Menu do botão R",
|
||||
"Install options": "Opções de instalação",
|
||||
"Install Options": "Opções de instalação",
|
||||
"Enable sysmmc": "Habilitar sysMMC",
|
||||
"Enable emummc": "Habilitar emuMMC",
|
||||
"Enable sysmmc": "Habilitar em sysMMC",
|
||||
"Enable emummc": "Habilitar em emuMMC",
|
||||
"Show install warning": "Mostrar aviso de instalação",
|
||||
"Install location": "Local de instalação",
|
||||
"System memory": "Memória do console",
|
||||
"microSD card": "Cartão microSD",
|
||||
"Boost CPU clock": "Impulsionar CPU",
|
||||
"Allow downgrade": "Permitir downgrade",
|
||||
"Skip if already installed": "Pular se já instalado",
|
||||
"Ticket only": "Instalar apenas tickets",
|
||||
@@ -211,31 +278,27 @@
|
||||
"Skip RSA header verify": "Pular checagens de header RSA",
|
||||
"Skip RSA NPDM verify": "Pular checagens de NPDM RSA",
|
||||
"Ignore distribution bit": "Ignorar bit de distribuição",
|
||||
"Convert to common ticket": "Converter para ticket comum",
|
||||
"Convert to standard crypto": "Converter para crypto padrão",
|
||||
"Lower master key": "Reduzir master keys",
|
||||
"Lower system version": "Reduzir versão do sistema",
|
||||
"Dump options": "Opções de exportação",
|
||||
"Dump Options": "Opções de exportação",
|
||||
"Created nested folder": "Criar pastas aninhadas",
|
||||
"Append folder with .xci": "Acrescentar .xci à pasta",
|
||||
"Trim XCI": "Aparar arquivos .xci",
|
||||
"Label trimmed XCI": "Rotular .xci aparados",
|
||||
"Multi-threaded USB transfer": "Trasfêrencia em multithread",
|
||||
|
||||
"Homebrew": "Aplicativos",
|
||||
"Apps": "Aplicativos",
|
||||
"Homebrew Options": "Opções de aplicativo",
|
||||
"Hide Sphaira": "Esconder sphaira",
|
||||
"Homebrew": "Homebrews",
|
||||
"Apps": "Homebrews",
|
||||
"Homebrew Options": "Opções do homebrew",
|
||||
"Hide Sphaira": "Esconder sphaira da lista",
|
||||
"Install Forwarder": "Instalar atalho forwarder",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "AVISO: Instalar atalhos forwarder pode resultar em um banimento!",
|
||||
"Installing Forwarder": "Instalando atalho forwarder...",
|
||||
"Creating Program": "Criando 'Program'",
|
||||
"Creating Control": "Criando 'Control'",
|
||||
"Creating Meta": "Criando 'Meta'",
|
||||
"Creating Program": "Criando \"Program\"",
|
||||
"Creating Control": "Criando \"Control\"",
|
||||
"Creating Meta": "Criando \"Meta\"",
|
||||
"Writing Nca": "Gravando NCA",
|
||||
"Updating ncm databse": "Atualizando base de dados NCM",
|
||||
"Updating ncm database": "Atualizando base de dados NCM",
|
||||
"Pushing application record": "Aplicando registro do software",
|
||||
"Failed to install forwarder": "Falha ao instalar atalho forwarder.",
|
||||
"Unstar": "Desfavoritar",
|
||||
"Star": "Favoritar",
|
||||
"Unstarred ": "Desfavoritado ",
|
||||
"Starred ": "Favoritado ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Falha ao desinstalar atalho forwarder, tente remove-lo manualmente.",
|
||||
@@ -248,11 +311,11 @@
|
||||
"Info": "Informações",
|
||||
"Changelog": "Alterações",
|
||||
"Details": "Detalhes",
|
||||
"version: %s": "versão: %s",
|
||||
"updated: %s": "atualizado: %s",
|
||||
"category: %s": "categoria: %s",
|
||||
"extracted: %.2f MiB": "tam. extraído: %.2f MiB",
|
||||
"app_dls: %s": "downloads: %s",
|
||||
"version: %s": "Versão: %s",
|
||||
"updated: %s": "Atualizado: %s",
|
||||
"category: %s": "Categoria: %s",
|
||||
"extracted: %.2f MiB": "Tamanho: %.2f MiB",
|
||||
"app_dls: %s": "№ de downloads: %s",
|
||||
"More by Author": "Mais deste autor",
|
||||
"Leave Feedback": "Deixar um feedback",
|
||||
|
||||
@@ -260,23 +323,23 @@
|
||||
"Files": "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",
|
||||
"File Options": "Opções do arquivo",
|
||||
"Show Hidden": "Mostrar arquivos ocultos",
|
||||
"Folders First": "Ordenar pastas primeiro",
|
||||
"Hidden Last": "Ordenar ocultos por último",
|
||||
"Split": "Dividir",
|
||||
"Close": "Fechar",
|
||||
"Cut": "Recortar",
|
||||
"Copy": "Copiar",
|
||||
"Copying ": "Copiando ",
|
||||
"Paste": "Colar",
|
||||
"Paste ": "Colar ",
|
||||
" file(s)?": " arquivo(s)?",
|
||||
"Paste file(s)?": "Colar arquivo(s)?",
|
||||
"Pasting ": "Colando ",
|
||||
"Pasting": "Colando ",
|
||||
"Rename": "Renomear",
|
||||
"Set New File Name": "Defina o nome do novo arquivo",
|
||||
"Extract zip": "Extrair zip",
|
||||
"Set New File Name": "Defina o nome do novo arquivo.",
|
||||
"Failed to delete directory": "Falha ao apagar diretório.",
|
||||
"Failed to delete file": "Falha ao apagar arquivo.",
|
||||
"Extract zip": "Extrair .zip",
|
||||
"Extract Options": "Opções de extração",
|
||||
"Extract here": "Extrair aqui",
|
||||
"Extract to root": "Extrair para a raiz",
|
||||
@@ -286,41 +349,43 @@
|
||||
"Extracting ": "Extraindo ",
|
||||
"Extract success!": "Extração concluída.",
|
||||
"Extract failed!": "Extração falhou.",
|
||||
"Compress to zip": "Comprimir em zip",
|
||||
"Compress Options": "Opções de compressão",
|
||||
"Compress to zip": "Comprimir em .zip",
|
||||
"Compress Options": "Comprimir em .zip",
|
||||
"Compress": "Comprimir",
|
||||
"Compress to...": "Comprimir para...",
|
||||
"Compressing ": "Comprimindo ",
|
||||
"Compress success!": "Compressão concluída.",
|
||||
"Compress failed!": "Compressão falhou.",
|
||||
"Create File": "Criar arquivo",
|
||||
"Set File Name": "Defina o nome do arquivo",
|
||||
"Set File Name": "Defina o nome do arquivo.",
|
||||
"Create Folder": "Criar pasta",
|
||||
"Set Folder Name": "Defina o nome da pasta",
|
||||
"Set Folder Name": "Defina o nome da pasta.",
|
||||
"Creating ": "Criando ",
|
||||
"View as text (unfinished)": "Ver como texto (inacabado)",
|
||||
"Upload": "Enviar",
|
||||
"Select upload location": "Selecione o local de envio",
|
||||
"No upload locations set!": "Nenhum local de envio definido.",
|
||||
"Uploading": "Enviando",
|
||||
"Upload successfull!": "Envio concluído.",
|
||||
"Upload failed!": "Envio falhou.",
|
||||
"Hash": "Hash",
|
||||
"Hash Options": "Opções de hash",
|
||||
"View as text (unfinished)": "Ver como texto (inacabado)",
|
||||
"Ignore read only": "Ignorar modo somente leitura",
|
||||
"Hash": "Calcular hash",
|
||||
"Hash Options": "Calcular hash",
|
||||
"Hashing": "Calculando hash...",
|
||||
"Failed to hash file...": "Falha ao calcular hash do arquivo.",
|
||||
"Ignore read only": "Ignorar \"somente leitura\"",
|
||||
"Mount": "Montar",
|
||||
"Sd": "SD",
|
||||
"Image System memory": "Imagem (memória do console)",
|
||||
"Image microSD card": "Imagem (cartão microSD)",
|
||||
"Empty...": "Vazio",
|
||||
"Empty...": "Vazio.",
|
||||
"Open with DayBreak?": "Abrir com DayBreak?",
|
||||
"Launch ": "Iniciar ",
|
||||
"Launch option for: ": "Opções de inicialização para: ",
|
||||
"Select launcher for: ": "Selecionar inicializador para: ",
|
||||
"Close FileBrowser?": "Fechar o gerenciador de arquivos?",
|
||||
"Close FileBrowser?": "Fechar gerenciador de arquivos?",
|
||||
|
||||
"Sort By": "Ordernar",
|
||||
"Sort Options": "Opções de ordenação",
|
||||
"Sort By": "Ordenar",
|
||||
"Sort Options": "Ordenar",
|
||||
"Filter": "Filtro",
|
||||
"All": "Todos",
|
||||
"Emulators": "Emuladores",
|
||||
@@ -334,8 +399,8 @@
|
||||
"Alphabetical (Star)": "Alfabética (favoritos)",
|
||||
"Updated": "Mais recentes",
|
||||
"Updated (Star)": "Mais recentes (favoritos)",
|
||||
"Downloads": "Nº de downloads",
|
||||
"Likes": "Nº de curtidas",
|
||||
"Downloads": "№ de downloads",
|
||||
"Likes": "№ de curtidas",
|
||||
"ID": "ID",
|
||||
"Order": "Ordem",
|
||||
"Descending": "Descendente",
|
||||
@@ -351,16 +416,16 @@
|
||||
"Search": "Buscar",
|
||||
|
||||
"Options": "Opções",
|
||||
"Split": "Dividir",
|
||||
"OK": "OK",
|
||||
"Back": "Voltar",
|
||||
"Select": "Selecionar",
|
||||
"Open": "Abrir",
|
||||
"Close": "Fechar",
|
||||
"Launch": "Iniciar",
|
||||
"Restart": "Reiniciar",
|
||||
"Next": "Prómixo",
|
||||
"Prev": "Anterior",
|
||||
"Unstar": "Desfavoritar",
|
||||
"Star": "Favoritar",
|
||||
"Yes": "Sim",
|
||||
"No": "Não",
|
||||
"On": "Sim",
|
||||
@@ -383,12 +448,13 @@
|
||||
"Remove": "Remover",
|
||||
"Completely remove ": "Remover completamente ",
|
||||
"Removing ": "Removendo ",
|
||||
"Removed ": "Removido ",
|
||||
"Uninstalling ": "Desinstalando ",
|
||||
"Removed ": "Removido ",
|
||||
|
||||
"Download": "Baixar",
|
||||
"Downloading ": "Baixando ",
|
||||
"Downloaded ": "Baixado ",
|
||||
"Download via the Network options!": "Baixe através das opções de rede.",
|
||||
|
||||
"Update": "Atualizar",
|
||||
"Update avaliable: ": "Atualização disponível: ",
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
"No Internet": "Нет интернета",
|
||||
"Switch-Handheld!": "Режим Портатива",
|
||||
"Switch-Docked!": "Режим Дока",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "Внимание! Включены логи, Sphaira будет работать медленно!",
|
||||
"Audio disabled due to suspended game": "Звук отключён из-за приостановки игры",
|
||||
"Are you sure you wish to cancel?": "Вы уверены, что хотите отменить?",
|
||||
"An error occurred": "Произошла ошибка",
|
||||
"If this message appears repeatedly, please open an issue.": "Если ошибка появляется неоднократно, пожалуйста, откройте issue",
|
||||
|
||||
"Menu Options": "Параметры меню",
|
||||
"Menu Options": "Опции меню",
|
||||
"Menu": "Меню",
|
||||
"Theme": "Тема",
|
||||
"Theme Options": "Параметры темы",
|
||||
"Theme Options": "Опции темы",
|
||||
"Select Theme": "Выбор темы",
|
||||
"Music": "Музыка",
|
||||
"12 Hour Time": "12-часовое время",
|
||||
@@ -20,13 +21,15 @@
|
||||
"Overwrite current default music?": "Переписать текущую стандартную музыку?",
|
||||
|
||||
"Network": "Сеть",
|
||||
"Network Options": "Параметры сети",
|
||||
"Network Options": "Опции сети",
|
||||
"Ftp": "FTP",
|
||||
"Mtp": "MTP",
|
||||
"Nxlink": "NXLink",
|
||||
"Nxlink Connected": "NXLink подключён",
|
||||
"Nxlink Upload": "NXLink отправка",
|
||||
"Nxlink Finished": "NXLink завершено",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "Защита HDD от записи",
|
||||
|
||||
"Language": "Язык",
|
||||
"Auto": "Авто",
|
||||
@@ -46,9 +49,9 @@
|
||||
"Ukrainian": "Українська",
|
||||
|
||||
"Misc": "Прочее",
|
||||
"Misc Options": "Прочие параметры",
|
||||
"Misc Options": "Прочие опции",
|
||||
"Games": "Игры",
|
||||
"Game Options": "Параметры игр",
|
||||
"Game Options": "Опции игр",
|
||||
"Hide forwarders": "Скрыть форвардеры",
|
||||
"Launch random game": "Запустить случайную игру",
|
||||
"List meta records": "Список мета-записей",
|
||||
@@ -57,25 +60,65 @@
|
||||
"No meta entries found...\n": "Мета-записей не найдено...\n",
|
||||
"Updating application record list": "Обновление списка записей приложений",
|
||||
"Dump": "Дамп",
|
||||
"Dump options": "Опции дампа",
|
||||
"Dump Options": "Опции дампа",
|
||||
"Select content to dump": "Выберите содержимое для дампа",
|
||||
"Dump All": "Дамп всего",
|
||||
"Dump Application": "Дамп приложения",
|
||||
"Dump Patch": "Дамп патча",
|
||||
"Dump AddOnContent": "Дамп дополнительного контента",
|
||||
"Dump DataPatch": "Дамп патча данных",
|
||||
"Created nested folder": "Создать вложенную папку",
|
||||
"Append folder with .xci": "Добавить папку с .xci",
|
||||
"Trim XCI": "Обрезать .xci",
|
||||
"Label trimmed XCI": "Пометить обрезанный .xci",
|
||||
"Multi-threaded USB transfer": "Многоядерная USB передача",
|
||||
"Dump All Bins": "Дамп всех BIN-файлов",
|
||||
"Dump XCI": "Дамп XCI",
|
||||
"Dump Card ID Set": "Дамп набора ID карты",
|
||||
"Dump Card UID": "Дамп UID карты",
|
||||
"Dump Certificate": "Дамп сертификата",
|
||||
"Dump Initial Data": "Дамп начальных данных",
|
||||
"Select dump location": "Выберите место для дампа",
|
||||
"microSD card (/dumps/NSP/)": "microSD карта (/dumps/NSP/)",
|
||||
"microSD card (/dumps/)": "microSD (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "Передача по USB (Switch 2 Switch)",
|
||||
"/dev/null (Speed Test)": "/dev/null (тест скорости)",
|
||||
"Dumping": "Снятие дампа",
|
||||
"Dump successfull!": "Дамп выполнен успешно!",
|
||||
"Dump failed!": "Сбой дампа!",
|
||||
"Success": "Успех",
|
||||
"Delete successfull!": "Удаление успешно!",
|
||||
"Delete failed!": "Ошибка удаления!",
|
||||
"Success": "Успех",
|
||||
"Title cache": "Кэш заголовков",
|
||||
|
||||
"Saves": "Сохранения",
|
||||
"Save Options": "Опции сохранений",
|
||||
"Account": "Пользователь",
|
||||
"Data Type": "Тип данных",
|
||||
"System": "Система",
|
||||
"BCAT": "BCAT",
|
||||
"Device": "Устройство",
|
||||
"Temporary": "Временные",
|
||||
"Cache": "Кэш",
|
||||
"System BCAT": "BCAT (Система)",
|
||||
"Backup": "Сделать бэкап",
|
||||
"Auto backup": "Автоматический бэкап",
|
||||
"Auto backup on restore": "Бэкап при восстановлении",
|
||||
"Compress backup": "Сжимать бэкапы",
|
||||
"Are you sure you want to backup save(s)?": "Вы уверены, что хотите сделать бэкап(ы)?",
|
||||
"No saves found in ": "Сохранения не найдены в ",
|
||||
"Backed up to ": "Бэкап сохранен в ",
|
||||
"Backup successfull!": "Бэкап успешно создан!",
|
||||
"Backup failed!": "Ошибка бэкапа!",
|
||||
"Select backup location": "Выберите место для бэкапа",
|
||||
"Select restore location": "Выберите место для восстановления",
|
||||
"Restore save for: ": "Восстановить сохранение для: ",
|
||||
"Are you sure you want to restore ": "Вы уверены, что хотите восстановить ",
|
||||
"Restore successfull!": "Восстановление успешно!",
|
||||
"Restore failed!": "Ошибка восстановления!",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Параметры Themezer",
|
||||
"Themezer Options": "Опции Themezer",
|
||||
"Nsfw": "NSFW",
|
||||
"Page": "Страница",
|
||||
"Page %zu / %zu": "Страница %zu / %zu",
|
||||
@@ -86,9 +129,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Загрузка json",
|
||||
"Select asset to download for ": "Выберите ресурс для загрузки: ",
|
||||
"Failed to download json": "Не удалось загрузить json",
|
||||
"Failed to download app!": "Не удалось загрузить приложение!",
|
||||
|
||||
"FTP Install": "Установка по FTP",
|
||||
"FTP Install (EXPERIMENTAL)": "Установка по FTP (ЭКСПЕРИМЕНТАЛЬНО)",
|
||||
"Connection Type: WiFi | Strength: ": "Тип подключения: WiFi | Сигнал: ",
|
||||
"Connection Type: Ethernet": "Тип подключения: Ethernet",
|
||||
"Connection Type: None": "Нет подключения",
|
||||
@@ -98,9 +142,26 @@
|
||||
"Password:": "Пароль:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "Пароль:",
|
||||
"Failed to install via FTP, press B to exit...": "Не удалось установить по FTP, нажмите B для выхода...",
|
||||
"Ftp install success!": "Установка по FTP прошла успешно!",
|
||||
"Ftp install failed!": "Сбой установки по FTP!",
|
||||
"Failed to install, press B to exit...": "Не удалось установить по FTP, нажмите B для выхода...",
|
||||
"Install success!": "Установка по FTP прошла успешно!",
|
||||
"Install failed!": "Сбой установки по FTP!",
|
||||
"MTP Install": "Установка по MTP",
|
||||
"State: %s | Speed: %s": "Состояние: %s | Скорость: %s",
|
||||
"Detached": "Отключено",
|
||||
"Attached": "Подключено",
|
||||
"Powered": "Заряжается",
|
||||
"Default": "По умолчанию",
|
||||
"Address": "Адрес",
|
||||
"Configured": "Настроено",
|
||||
"Suspended": "Приостановлено",
|
||||
"USB 1.0 Low Speed": "USB 1.0",
|
||||
"USB 1.1 Full Speed": "USB 1.1",
|
||||
"USB 2.0 High Speed": "USB 2.0",
|
||||
"USB 3.0 Super Speed": "USB 3.0",
|
||||
"Drag'n'Drop (NSP, XCI, NSZ, XCZ) to the install folder": "Перетащите (NSP, XCI, NSZ, XCZ) в папку установки",
|
||||
"Failed to install via MTP, press B to exit...": "Не удалось установить по MTP, нажмите B для выхода...",
|
||||
"MTP install success!": "Установка по MTP успешна!",
|
||||
"MTP install failed!": "Сбой установки по MTP!",
|
||||
"USB Install": "Установка по USB",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "Подключено, ожидание списка файлов...",
|
||||
@@ -120,11 +181,14 @@
|
||||
"GameCard Install": "Установка с картриджа",
|
||||
"GameCard": "Картридж",
|
||||
"GC": "GC",
|
||||
"System memory %.1f GB": "Память системы %.1f ГБ",
|
||||
"microSD card %.1f GB": "Карта microSD %.1f ГБ",
|
||||
"Nand Install": "Установка в NAND",
|
||||
"SD Card Install": "Установка на SD карту",
|
||||
"System memory %.1f GB": "NAND %.1f ГБ",
|
||||
"microSD card %.1f GB": "microSD %.1f ГБ",
|
||||
"Exit": "Выход",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Установка отключена...\nПожалуйста, включите установку в опциях установки.",
|
||||
"No GameCard inserted": "Картридж не вставлен",
|
||||
"GameCard is already trimmed!": "Картридж уже обрезан!",
|
||||
"WARNING: GameCard is already trimmed!": "ВНИМАНИЕ: Картридж уже обрезан!",
|
||||
"Continue": "Продолжить",
|
||||
"Gc install success!": "Установка с картриджа успешна!",
|
||||
"Gc install failed!": "Сбой установки с картриджа!",
|
||||
|
||||
@@ -160,17 +224,17 @@
|
||||
"Negative image": "Включен",
|
||||
"Format": "Разрешение",
|
||||
"Trimming Format": "Обрезка",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Внешний светофильтр",
|
||||
"Load Default": "По умолчанию",
|
||||
|
||||
"Web": "Браузер",
|
||||
"Select URL": "Выберите URL",
|
||||
"Enter custom URL": "Введите свой URL",
|
||||
"Enter URL": "Введите URL",
|
||||
|
||||
"Advanced": "Продвинутые",
|
||||
"Advanced Options": "Расширенные параметры",
|
||||
"Logging": "Журналирование",
|
||||
"Advanced Options": "Расширенные опции",
|
||||
"Logging": "Логи",
|
||||
"Replace hbmenu on exit": "Замена hbmenu при выходе",
|
||||
"Restore hbmenu?": "Восстановить hbmenu?",
|
||||
"Restore": "Восстановить",
|
||||
@@ -181,20 +245,21 @@
|
||||
"Restored hbmenu": "hbmenu восстановлен",
|
||||
"Restart Sphaira?": "Перезапустить Sphaira?",
|
||||
"Press OK to restart Sphaira": "Нажмите OK для перезапуска Sphaira",
|
||||
"Boost CPU during transfer": "Разгон CPU при передаче",
|
||||
"Text scroll speed": "Скорость текста",
|
||||
"Slow": "Медленная",
|
||||
"Normal": "Обычная",
|
||||
"Fast": "Быстрая",
|
||||
"Set left-side menu": "Меню слева",
|
||||
"Set right-side menu": "Меню справа",
|
||||
"Install options": "Параметры установки",
|
||||
"Install Options": "Параметры установки",
|
||||
"Install options": "Опции установки",
|
||||
"Install Options": "Опции установки",
|
||||
"Enable sysmmc": "Включить на сиснанде",
|
||||
"Enable emummc": "Включить на эмунанде",
|
||||
"Show install warning": "Предупрежд. при установке",
|
||||
"Install location": "Место установки",
|
||||
"System memory": "NAND",
|
||||
"microSD card": "microSD",
|
||||
"Boost CPU clock": "Использовать разгон CPU",
|
||||
"Allow downgrade": "Разрешить даунгрейд",
|
||||
"Skip if already installed": "Пропуск установленного",
|
||||
"Ticket only": "Только тикет",
|
||||
@@ -203,28 +268,31 @@
|
||||
"Skip dlc": "Пропустить DLC",
|
||||
"Skip data patch": "Пропустить патч данных",
|
||||
"Skip ticket": "Пропустить тикет",
|
||||
"skip NCA hash verify": "Не проверять NCA hash",
|
||||
"Skip NCA hash verify": "Не проверять NCA hash",
|
||||
"Skip RSA header verify": "Не проверять RSA header",
|
||||
"Skip RSA NPDM verify": "Не проверять RSA NPDM",
|
||||
"Ignore distribution bit": "Игнор. бит распределения",
|
||||
"Ignore distribution bit": "Игнор. битa распределения",
|
||||
"Convert to common ticket": "Конверт. в общий тикет",
|
||||
"Convert to standard crypto": "Конверт. в стандарт. крипт.",
|
||||
"Lower master key": "Снизить мастер-ключ",
|
||||
"Lower system version": "Снизить версию системы",
|
||||
|
||||
"Homebrew": "Homebrew",
|
||||
"Apps": "Приложения",
|
||||
"Homebrew Options": "Параметры Homebrew",
|
||||
"Homebrew Options": "Опции Homebrew",
|
||||
"Hide Sphaira": "Скрыть Sphaira",
|
||||
"Install Forwarder": "Установить форвардер",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "ВНИМАНИЕ: \nУстановка в сиснанд приведет к бану!",
|
||||
"Installing Forwarder": "Установить форвардер",
|
||||
"Installing Forwarder": "Установка форвардера",
|
||||
"Creating Program": "Создание программы",
|
||||
"Creating Control": "Создание элемента управления",
|
||||
"Creating Meta": "Создание меты",
|
||||
"Writing Nca": "Запись NCA",
|
||||
"Updating ncm databse": "Обновление базы данных NCM",
|
||||
"Updating ncm database": "Обновление базы данных NCM",
|
||||
"Pushing application record": "Добавление записи приложения",
|
||||
"Failed to install forwarder": "Не удалось установить форвардер",
|
||||
"Unstar": "Убрать из избранного",
|
||||
"Star": "Добавить в избранное",
|
||||
"Unstarred ": "Удалено из избранного: ",
|
||||
"Starred ": "Добавлено в избранное: ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Не удалось удалить старый форвардер, удалите его вручную!",
|
||||
@@ -232,8 +300,8 @@
|
||||
"AppStore": "AppStore",
|
||||
"Appstore": "AppStore",
|
||||
"Store": "Магазин",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Показывать: %s | Сорт: %s | Порядок: %s",
|
||||
"AppStore Options": "Параметры AppStore",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Показывать: %s | Сортировка: %s | Порядок: %s",
|
||||
"AppStore Options": "Опции AppStore",
|
||||
"Info": "Информация",
|
||||
"Changelog": "Список изменений",
|
||||
"Details": "Подробности",
|
||||
@@ -242,29 +310,32 @@
|
||||
"category: %s": "категория: %s",
|
||||
"extracted: %.2f MiB": "извлечено: %.2f MiB",
|
||||
"app_dls: %s": "app_dls: %s",
|
||||
"More by Author": "Другие приложения автора",
|
||||
"More by Author": "Другое от автора",
|
||||
"Leave Feedback": "Оставить отзыв",
|
||||
"Visit Website": "Посетить сайт",
|
||||
|
||||
"FileBrowser": "Файловый менеджер",
|
||||
"FileBrowser": "Файлы",
|
||||
"Files": "Файлы",
|
||||
"%zd files": "%zd файлы",
|
||||
"%zd dirs": "%zd разделы",
|
||||
"File Options": "Параметры файла",
|
||||
"File Options": "Опции файла",
|
||||
"Show Hidden": "Показать скрытые",
|
||||
"Folders First": "Папки в начале",
|
||||
"Hidden Last": "Скрытые в конце",
|
||||
"Split": "Разделить",
|
||||
"Cut": "Вырезать",
|
||||
"Copy": "Копировать",
|
||||
"Copying ": "Копирование ",
|
||||
"Paste": "Вставить",
|
||||
"Paste ": "Вставить ",
|
||||
" file(s)?": " файл(ов)?",
|
||||
"Paste file(s)?": "Вставить файл(ы)?",
|
||||
"Pasting ": "Вставка ",
|
||||
"Pasting": "Вставка",
|
||||
"Rename": "Переименовать",
|
||||
"Set New File Name": "Задайте новое имя файла",
|
||||
"Failed to delete directory": "Не удалось удалить папку",
|
||||
"Failed to delete file": "Не удалось удалить файл",
|
||||
"Extract zip": "Распаковать .zip",
|
||||
"Extract Options": "Параметры распаковки",
|
||||
"Extract Options": "Опции распаковки",
|
||||
"Extract here": "Распаковать сюда",
|
||||
"Extract to root": "Распаковать в корень",
|
||||
"Are you sure you want to extract to root?": "Вы уверены, что хотите распаковать в корень?",
|
||||
@@ -274,7 +345,7 @@
|
||||
"Extract success!": "Распаковка успешна!",
|
||||
"Extract failed!": "Сбой распаковки!",
|
||||
"Compress to zip": "Сжать в .zip",
|
||||
"Compress Options": "Параметры сжатия",
|
||||
"Compress Options": "Опции сжатия",
|
||||
"Compress": "Сжать",
|
||||
"Compress to...": "Сжать в...",
|
||||
"Compressing ": "Сжатие ",
|
||||
@@ -285,32 +356,37 @@
|
||||
"Create Folder": "Создать папку",
|
||||
"Set Folder Name": "Укажите имя папки",
|
||||
"Creating ": "Создание ",
|
||||
"View as text (unfinished)": "Открыть как текст (не доделано)",
|
||||
"Upload": "Отправить",
|
||||
"Select upload location": "Выберите место загрузки",
|
||||
"No upload locations set!": "Места загрузки не заданы!",
|
||||
"Uploading": "Отправка",
|
||||
"Upload successfull!": "Отправка успешна!",
|
||||
"Upload failed!": "Сбой Отправки!",
|
||||
"View as text (unfinished)": "Открыть как текст (не доделано)",
|
||||
"Hash": "Хэш",
|
||||
"Hash Options": "Опции хэша",
|
||||
"Hashing": "Вычисление хэша",
|
||||
"Failed to hash file...": "Не удалось вычислить хэш файла...",
|
||||
"Ignore read only": "Игнор. только для чтения",
|
||||
"Mount": "Монтирован",
|
||||
"Mount": "Монтировать",
|
||||
"Sd": "Micro SD",
|
||||
"Image System memory": "Альбом Сиснанда",
|
||||
"Image microSD card": "Альбом Эмунанда",
|
||||
"Empty...": "Пусто...",
|
||||
"Open with DayBreak?": "Открыть с помощью DayBreak?",
|
||||
"Launch ": "Запуск ",
|
||||
"Launch option for: ": "Параметры запуска для: ",
|
||||
"Launch option for: ": "Опции запуска для: ",
|
||||
"Select launcher for: ": "Выберите загрузчик для: ",
|
||||
"Close FileBrowser?": "Закрыть Файлы?",
|
||||
|
||||
"Sort By": "Сортировка",
|
||||
"Sort Options": "Параметры сортировки",
|
||||
"Sort Options": "Опции сортировки",
|
||||
"Filter": "Показывать",
|
||||
"All": "Всё",
|
||||
"Emulators": "Эмуляторы",
|
||||
"Tools": "Инструменты",
|
||||
"Themes": "Темы",
|
||||
"Legacy": "Старые",
|
||||
"Legacy": "Легаси",
|
||||
"Sort": "Сортировать",
|
||||
"Size": "По размеру",
|
||||
"Size (Star)": "По размеру (избр.)",
|
||||
@@ -334,17 +410,17 @@
|
||||
"Grid": "Сетка",
|
||||
"Search": "Поиск",
|
||||
|
||||
"Options": "Параметры",
|
||||
"Options": "Опции",
|
||||
"Split": "Разделить",
|
||||
"OK": "ОК",
|
||||
"Back": "Назад",
|
||||
"Select": "Выбрать",
|
||||
"Open": "Открыть",
|
||||
"Close": "Закрыть",
|
||||
"Launch": "Запуск",
|
||||
"Restart": "Перезапустить",
|
||||
"Next": "Далее",
|
||||
"Prev": "Назад",
|
||||
"Unstar": "Убрать из избранного",
|
||||
"Star": "Добавить в избранное",
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"On": "Вкл",
|
||||
@@ -367,12 +443,13 @@
|
||||
"Remove": "Удалить",
|
||||
"Completely remove ": "Полностью удалить ",
|
||||
"Removing ": "Удаляется ",
|
||||
"Removed ": "Удалено ",
|
||||
"Uninstalling ": "Деинсталляция ",
|
||||
"Removed ": "Удалено ",
|
||||
|
||||
"Download": "Скачать",
|
||||
"Downloading ": "Загрузка ",
|
||||
"Downloaded ": "Загружено ",
|
||||
"Download via the Network options!": "Скачайте через: Меню - Сеть",
|
||||
|
||||
"Update": "Обновить",
|
||||
"Update avaliable: ": "Доступно обновление: ",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "Ingen Internetanslutning",
|
||||
"Switch-Handheld!": "Switch Handhållen!",
|
||||
"Switch-Docked!": "Switch Dockad!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "",
|
||||
"Audio disabled due to suspended game": "Ljud är avstängt på grund av bakgrundsprogram",
|
||||
"Are you sure you wish to cancel?": "Är du säker på att du vill avbryta?",
|
||||
"An error occurred": "",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink ansluten",
|
||||
"Nxlink Upload": "Nxlink överför",
|
||||
"Nxlink Finished": "Nxlink klar",
|
||||
"Hdd": "",
|
||||
"Hdd write protect": "",
|
||||
|
||||
"Language": "Språk",
|
||||
"Auto": "Auto",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Dump options": "",
|
||||
"Dump Options": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Created nested folder": "",
|
||||
"Append folder with .xci": "",
|
||||
"Trim XCI": "",
|
||||
"Label trimmed XCI": "",
|
||||
"Multi-threaded USB transfer": "",
|
||||
"Dump All Bins": "",
|
||||
"Dump XCI": "",
|
||||
"Dump Card ID Set": "",
|
||||
"Dump Card UID": "",
|
||||
"Dump Certificate": "",
|
||||
"Dump Initial Data": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"microSD card (/dumps/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Success": "",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Themezer-alternativ",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Laddar ner JSON",
|
||||
"Select asset to download for ": "Välj tillgång att ladda ner för ",
|
||||
"Failed to download json": "",
|
||||
"Failed to download app!": "",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"Failed to install, press B to exit...": "",
|
||||
"Install success!": "",
|
||||
"Install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "",
|
||||
"No GameCard inserted": "",
|
||||
"GameCard is already trimmed!": "",
|
||||
"WARNING: GameCard is already trimmed!": "",
|
||||
"Continue": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "Negativ bild",
|
||||
"Format": "Format",
|
||||
"Trimming Format": "Trimningsformat",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Externt ljusfilter",
|
||||
"Load Default": "Ladda standard",
|
||||
|
||||
"Web": "",
|
||||
"Select URL": "",
|
||||
"Enter custom URL": "",
|
||||
"Enter URL": "",
|
||||
|
||||
"Advanced": "Avancerat",
|
||||
"Advanced Options": "Avancerade alternativ",
|
||||
"Logging": "Loggning",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "Återställde hbmenu.",
|
||||
"Restart Sphaira?": "Starta om Sphaira?",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Boost CPU during transfer": "",
|
||||
"Text scroll speed": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Set left-side menu": "",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "Installationsplats",
|
||||
"System memory": "Systemminne",
|
||||
"microSD card": "microSD-kort",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "Skapar kontroll",
|
||||
"Creating Meta": "Skapar metadata",
|
||||
"Writing Nca": "Skriver Nca",
|
||||
"Updating ncm databse": "Uppdaterar ncm-databas",
|
||||
"Updating ncm database": "Uppdaterar ncm-databas",
|
||||
"Pushing application record": "Skickar programpost",
|
||||
"Failed to install forwarder": "Misslyckades att installera genväg",
|
||||
"Unstar": "Avmarkera stjärna",
|
||||
"Star": "Markera stjärna",
|
||||
"Unstarred ": "Avmarkerad ",
|
||||
"Starred ": "Markerad ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Kopiera",
|
||||
"Copying ": "Kopierar ",
|
||||
"Paste": "Klistra in",
|
||||
"Paste ": "Klistra in ",
|
||||
" file(s)?": " fil(er)?",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "Klistrar in ",
|
||||
"Pasting": "Klistrar in",
|
||||
"Rename": "Byt namn",
|
||||
"Set New File Name": "Ange nytt filnamn",
|
||||
"Failed to delete directory": "",
|
||||
"Failed to delete file": "",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Skapa mapp",
|
||||
"Set Folder Name": "Ange mappnamn",
|
||||
"Creating ": "Skapar ",
|
||||
"View as text (unfinished)": "Visa som text (ofärdig)",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Visa som text (ofärdig)",
|
||||
"Hash": "",
|
||||
"Hash Options": "",
|
||||
"Hashing": "",
|
||||
"Failed to hash file...": "",
|
||||
"Ignore read only": "Ignorera skrivskydd",
|
||||
"Mount": "Montera",
|
||||
"Sd": "Sd",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "Starta ",
|
||||
"Launch option for: ": "Startalternativ för: ",
|
||||
"Select launcher for: ": "Välj startprogram för: ",
|
||||
"Close FileBrowser?": "",
|
||||
|
||||
"Sort By": "Sortera efter",
|
||||
"Sort Options": "Sorteringsalternativ",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "Sök",
|
||||
|
||||
"Options": "Alternativ",
|
||||
"Split": "",
|
||||
"OK": "OK",
|
||||
"Back": "Tillbaka",
|
||||
"Select": "Välj",
|
||||
"Open": "Öppna",
|
||||
"Close": "",
|
||||
"Launch": "Starta",
|
||||
"Restart": "Starta om",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "Avmarkera stjärna",
|
||||
"Star": "Markera stjärna",
|
||||
"Yes": "Ja",
|
||||
"No": "Nej",
|
||||
"On": "",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Ta bort",
|
||||
"Completely remove ": "Ta bort helt ",
|
||||
"Removing ": "Tar bort ",
|
||||
"Removed ": "Borttagen ",
|
||||
"Uninstalling ": "Avinstallerar ",
|
||||
"Removed ": "Borttagen ",
|
||||
|
||||
"Download": "Ladda ner",
|
||||
"Downloading ": "Laddar ner ",
|
||||
"Downloaded ": "Nedladdad ",
|
||||
"Download via the Network options!": "",
|
||||
|
||||
"Update": "Uppdatera",
|
||||
"Update avaliable: ": "Uppdatering tillgänglig: ",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "Tomt!",
|
||||
"Not Ready...": "Inte redo...",
|
||||
"Error loading page!": "Fel vid laddning av sida!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"[Applet Mode]": "[Режим Аплету]",
|
||||
"[Applet Mode]": "[Аплет]",
|
||||
"No Internet": "Без інтернету",
|
||||
"Switch-Handheld!": "Switch - Портатив!",
|
||||
"Switch-Docked!": "Switch - Докований!",
|
||||
"Switch-Docked!": "Switch - Док!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "Увага! Логи увімкнені, Sphaira працюватиме повільно!",
|
||||
"Audio disabled due to suspended game": "Аудіо вимкнено через призупинену програму",
|
||||
"Are you sure you wish to cancel?": "Ви впевнені, що хочете скасувати?",
|
||||
"An error occurred": "",
|
||||
"An error occurred": "Сталася помилка",
|
||||
"If this message appears repeatedly, please open an issue.": "Якщо це повідомлення з'являється повторно, будь ласка, повідомте про проблему.",
|
||||
|
||||
"Menu Options": "Опції меню",
|
||||
@@ -16,8 +17,8 @@
|
||||
"Music": "Музика",
|
||||
"12 Hour Time": "12-годинний формат часу",
|
||||
"Download Default Music": "Завантажити музику за замовчуванням",
|
||||
"Failed to download default_music.bfstm, please try again": "",
|
||||
"Overwrite current default music?": "",
|
||||
"Failed to download default_music.bfstm, please try again": "Не вдалося завантажити default_music.bfstm, спробуйте ще раз",
|
||||
"Overwrite current default music?": "Перезаписати поточну музику за замовчуванням?",
|
||||
|
||||
"Network": "Мережа",
|
||||
"Network Options": "Опції мережі",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink підключено",
|
||||
"Nxlink Upload": "Nxlink | Завантаження",
|
||||
"Nxlink Finished": "Nxlink | Завершено",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "Захист HDD від запису",
|
||||
|
||||
"Language": "Мова",
|
||||
"Auto": "Автоматично",
|
||||
@@ -53,26 +56,39 @@
|
||||
"Launch random game": "Запустити випадкову гру",
|
||||
"List meta records": "Список метаданих записів",
|
||||
"Entries": "Записи",
|
||||
"Failed to list application meta entries": "",
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Failed to list application meta entries": "Не вдалося вивести список метаданих програм",
|
||||
"No meta entries found...\n": "Не знайдено метаданих...\n",
|
||||
"Updating application record list": "Оновлення списку записів програм",
|
||||
"Dump": "Дамп",
|
||||
"Dump options": "Опції дампу",
|
||||
"Dump Options": "Опції дампу",
|
||||
"Select content to dump": "Виберіть вміст для дампу",
|
||||
"Dump All": "Дамп всього",
|
||||
"Dump Application": "Дамп програми",
|
||||
"Dump Patch": "Дамп патчу",
|
||||
"Dump AddOnContent": "Дамп DLC",
|
||||
"Dump DataPatch": "Дамп патчу даних",
|
||||
"Created nested folder": "Створено вкладену теку",
|
||||
"Append folder with .xci": "Додати до теки .xci",
|
||||
"Trim XCI": "Обрізати XCI",
|
||||
"Label trimmed XCI": "Позначити обрізаний XCI",
|
||||
"Multi-threaded USB transfer": "Багатопотокова передача USB",
|
||||
"Dump All Bins": "Дамп всіх BIN-файлів",
|
||||
"Dump XCI": "Дамп XCI",
|
||||
"Dump Card ID Set": "Дамп набору ID картриджа",
|
||||
"Dump Card UID": "Дамп UID картриджа",
|
||||
"Dump Certificate": "Дамп сертифіката",
|
||||
"Dump Initial Data": "Дамп початкових даних",
|
||||
"Select dump location": "Виберіть місце для дампу",
|
||||
"microSD card (/dumps/)": "SD-карта (/dumps/)",
|
||||
"USB transfer (Switch 2 Switch)": "Передача USB (Switch <-> Switch)",
|
||||
"/dev/null (Speed Test)": "Тест швидкості (/dev/null)",
|
||||
"Dumping": "Дампінг",
|
||||
"Dump successfull!": "Дамп успішний!",
|
||||
"Dump failed!": "Помилка дампу!",
|
||||
"Delete successfull!": "Видалення успішне!",
|
||||
"Delete failed!": "Помилка видалення!",
|
||||
"Success": "Успіх",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Опції Themezer",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Завантаження JSON",
|
||||
"Select asset to download for ": "Виберіть ресурс для завантаження для ",
|
||||
"Failed to download json": "Не вдалося завантажити JSON",
|
||||
"Failed to download app!": "Не вдалося завантажити програму!",
|
||||
|
||||
"FTP Install": "Встановлення через FTP",
|
||||
"FTP Install (EXPERIMENTAL)": "Встановлення через FTP (ЕКСПЕРИМЕНТАЛЬНО)",
|
||||
"Connection Type: WiFi | Strength: ": "Тип підключення: WiFi | Сила сигналу: ",
|
||||
"Connection Type: Ethernet": "Тип підключення: Ethernet",
|
||||
"Connection Type: None": "Тип підключення: Немає",
|
||||
@@ -98,19 +115,19 @@
|
||||
"Password:": "Пароль:",
|
||||
"SSID:": "SSID:",
|
||||
"Passphrase:": "Кодова фраза:",
|
||||
"Failed to install via FTP, press B to exit...": "Не вдалося встановити через FTP, натисніть B для виходу...",
|
||||
"Ftp install success!": "Встановлення через FTP успішно завершено.",
|
||||
"Ftp install failed!": "Встановлення через FTP не вдалося.",
|
||||
"Failed to install, press B to exit...": "Не вдалося встановити через FTP, натисніть B для виходу...",
|
||||
"Install success!": "Успішно встановлено",
|
||||
"Install failed!": "Встановлення не вдалося.",
|
||||
"USB Install": "Встановлення через USB",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "",
|
||||
"Connected, starting transfer...": "",
|
||||
"Connected, waiting for file list...": "Підключено, очікування списку файлів...",
|
||||
"Connected, starting transfer...": "Підключено, початок передачі...",
|
||||
"Failed to init usb, press B to exit...": "Не вдалося ініціалізувати USB, натисніть B для виходу...",
|
||||
"Waiting for connection...": "Очікування підключення...",
|
||||
"Transferring data...": "Передача даних...",
|
||||
"USB connected, sending file list": "",
|
||||
"Sent file list, waiting for command...": "",
|
||||
"waiting for usb connection...": "",
|
||||
"USB connected, sending file list": "USB підключено, надсилання списку файлів",
|
||||
"Sent file list, waiting for command...": "Список файлів надіслано, очікування команди...",
|
||||
"waiting for usb connection...": "очікування USB підключення...",
|
||||
"Disable MTP for usb install": "Вимкнути MTP для встановлення через USB",
|
||||
"Re-enabled MTP": "MTP знову увімкнено",
|
||||
"Installed via usb": "Встановлено через USB",
|
||||
@@ -119,17 +136,20 @@
|
||||
"Press B to exit...": "Натисніть B для виходу...",
|
||||
"GameCard Install": "Встановлення з картриджа",
|
||||
"GameCard": "Картридж",
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"GC": "К",
|
||||
"System memory %.1f GB": "Пам'ять консолі %.1f ГБ",
|
||||
"microSD card %.1f GB": "SD-карта %.1f ГБ",
|
||||
"Exit": "Вихід",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "Встановлення вимкнено...\nБудь ласка, увімкніть встановлення в опціях.",
|
||||
"No GameCard inserted": "Картридж не вставлено",
|
||||
"GameCard is already trimmed!": "Картридж вже обрізано!",
|
||||
"WARNING: GameCard is already trimmed!": "УВАГА: Картридж вже обрізано!",
|
||||
"Continue": "Продовжити",
|
||||
"Gc install success!": "Встановлення з картриджа успішно завершено.",
|
||||
"Gc install failed!": "Встановлення з картриджа не вдалося.",
|
||||
|
||||
"IRS (Infrared Joycon Camera)": "ІЧ (Інфрачервона камера Joycon)",
|
||||
"IRS": "",
|
||||
"IRS": "ІЧ",
|
||||
"Irs": "ІЧ-сенсор",
|
||||
"Ambient Noise Level: ": "Рівень навколишнього шуму: ",
|
||||
"Controller": "Контролер",
|
||||
@@ -157,17 +177,17 @@
|
||||
"Gain": "Підсилення",
|
||||
"Negative Image": "Негативне зображення",
|
||||
"Normal image": "Нормальне зображення",
|
||||
"Negative image": "",
|
||||
"Negative image": "Негативне зображення",
|
||||
"Format": "Формат",
|
||||
"Trimming Format": "Формат обрізки",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Фільтр зовнішнього освітлення",
|
||||
"Load Default": "Завантажити типові",
|
||||
|
||||
"Web": "Веб",
|
||||
"Select URL": "Вибрати URL",
|
||||
"Enter custom URL": "Ввести власний URL",
|
||||
"Enter URL": "Ввести URL",
|
||||
|
||||
"Advanced": "Додатково",
|
||||
"Advanced Options": "Додаткові опції",
|
||||
"Logging": "Логування",
|
||||
@@ -181,37 +201,38 @@
|
||||
"Restored hbmenu": "hbmenu відновлено",
|
||||
"Restart Sphaira?": "Перезапустити Sphaira?",
|
||||
"Press OK to restart Sphaira": "Натисніть OK для перезапуску Sphaira",
|
||||
"Boost CPU during transfer": "Розгін при передачі",
|
||||
"Text scroll speed": "Швидк. прокрутки",
|
||||
"Slow": "Повільно",
|
||||
"Normal": "Нормально",
|
||||
"Normal": "Середнє",
|
||||
"Fast": "Швидко",
|
||||
"Set right-side menu": "Праве меню",
|
||||
"Set left-side menu": "Ліве меню (L)",
|
||||
"Set right-side menu": "Праве меню (R)",
|
||||
"Install options": "Опції встановлення",
|
||||
"Install Options": "Опції встановлення",
|
||||
"Enable sysmmc": "",
|
||||
"Enable emummc": "",
|
||||
"Enable sysmmc": "Дозволити в sysMMC",
|
||||
"Enable emummc": "Дозволити в emuMMC",
|
||||
"Show install warning": "Попередж. при встанов.",
|
||||
"Install location": "Місце встановлення",
|
||||
"System memory": "Пам'ять консолі",
|
||||
"microSD card": "SD-карта",
|
||||
"Boost CPU clock": "Розігнати CPU",
|
||||
"Install location": "Ставити в",
|
||||
"System memory": "NAND",
|
||||
"microSD card": "microSD",
|
||||
"Allow downgrade": "Дозволити відкат",
|
||||
"Skip if already installed": "Пропуск, якщо встановл.",
|
||||
"Skip if already installed": "Пропуск встановленого",
|
||||
"Ticket only": "Тільки тікет",
|
||||
"Skip base": "Пропустити базу",
|
||||
"Skip patch": "Пропустити патч",
|
||||
"Skip dlc": "Пропустити DLC",
|
||||
"Skip data patch": "Пропустити патч даних",
|
||||
"Skip ticket": "Пропустити тікет",
|
||||
"Skip NCA hash verify": "",
|
||||
"Skip RSA header verify": "Пропуск перевірку заголовка RSA",
|
||||
"Skip RSA NPDM verify": "Пропуск перевірку NPDM RSA",
|
||||
"Ignore distribution bit": "Ігнорувати біт розподілу",
|
||||
"Convert to standard crypto": "Конвертувати у стандартне шифрування",
|
||||
"Skip NCA hash verify": "Не перевір. NCA hash",
|
||||
"Skip RSA header verify": "Не перевір. RSA header",
|
||||
"Skip RSA NPDM verify": "Не перевір. NPDM RSA",
|
||||
"Ignore distribution bit": "Ігнор. біт розподілу",
|
||||
"Convert to standard crypto": "Конверт. у стандарт. шифр.",
|
||||
"Lower master key": "Знизити майстер-ключ",
|
||||
"Lower system version": "Знизити версію системи",
|
||||
|
||||
"Homebrew": "Домашні програми",
|
||||
"Homebrew": "Homebrew",
|
||||
"Apps": "Програми",
|
||||
"Homebrew Options": "Опції домашніх програм",
|
||||
"Hide Sphaira": "Приховати Sphaira",
|
||||
@@ -222,15 +243,17 @@
|
||||
"Creating Control": "Створення контролера",
|
||||
"Creating Meta": "Створення метаданих",
|
||||
"Writing Nca": "Запис NCA",
|
||||
"Updating ncm databse": "Оновлення бази даних NCM",
|
||||
"Updating ncm database": "Оновлення бази даних NCM",
|
||||
"Pushing application record": "Запис даних програми",
|
||||
"Failed to install forwarder": "Не вдалося встановити форвардер",
|
||||
"Unstar": "Прибрати з обраного",
|
||||
"Star": "Позначити зіркою",
|
||||
"Unstarred ": "Знято зірку з ",
|
||||
"Starred ": "Позначено зіркою ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
"Failed to remove old forwarder, please manually remove it!": "Не вдалося видалити старий форвардер, видаліть його вручну!",
|
||||
|
||||
"AppStore": "Магазин програм",
|
||||
"Appstore": "",
|
||||
"AppStore": "AppStore",
|
||||
"Appstore": "AppStore",
|
||||
"Store": "Магазин",
|
||||
"Filter: %s | Sort: %s | Order: %s": "Фільтр: %s | Сорт.: %s | Порядок: %s",
|
||||
"AppStore Options": "Опції магазину програм",
|
||||
@@ -257,41 +280,46 @@
|
||||
"Copy": "Копіювати",
|
||||
"Copying ": "Копіювання ",
|
||||
"Paste": "Вставити",
|
||||
"Paste ": "Вставити: ",
|
||||
" file(s)?": " файл(и)?",
|
||||
"Paste file(s)?": "Вставити файл(и)?",
|
||||
"Pasting ": "Вставлення ",
|
||||
"Pasting": "Вставлення",
|
||||
"Rename": "Перейменувати",
|
||||
"Set New File Name": "Введіть нове ім'я файлу",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
"Extract to root": "",
|
||||
"Are you sure you want to extract to root?": "",
|
||||
"Extract to...": "",
|
||||
"Enter the path to the folder to extract into": "",
|
||||
"Extracting ": "",
|
||||
"Extract success!": "",
|
||||
"Extract failed!": "",
|
||||
"Failed to delete directory": "Не вдалося видалити теку",
|
||||
"Failed to delete file": "Не вдалося видалити файл",
|
||||
"Extract zip": "Розпакувати zip",
|
||||
"Extract Options": "Опції розпакування",
|
||||
"Extract here": "Розпакувати тут",
|
||||
"Extract to root": "Розпакувати в корінь",
|
||||
"Are you sure you want to extract to root?": "Ви впевнені, що хочете розпакувати в корінь?",
|
||||
"Extract to...": "Розпакувати в...",
|
||||
"Enter the path to the folder to extract into": "Введіть шлях до теки для розпакування",
|
||||
"Extracting ": "Розпакування ",
|
||||
"Extract success!": "Розпакування успішне!",
|
||||
"Extract failed!": "Помилка розпакування!",
|
||||
"Compress to zip": "Стиснути в zip",
|
||||
"Compress Options": "",
|
||||
"Compress": "",
|
||||
"Compress to...": "",
|
||||
"Compressing ": "",
|
||||
"Compress success!": "",
|
||||
"Compress failed!": "",
|
||||
"Compress Options": "Опції стиснення",
|
||||
"Compress": "Стиснути",
|
||||
"Compress to...": "Стиснути в...",
|
||||
"Compressing ": "Стиснення ",
|
||||
"Compress success!": "Стиснення успішне!",
|
||||
"Compress failed!": "Помилка стиснення!",
|
||||
"Create File": "Створити файл",
|
||||
"Set File Name": "Введіть ім'я файлу",
|
||||
"Create Folder": "Створити теку",
|
||||
"Set Folder Name": "Введіть ім'я теки",
|
||||
"Creating ": "Створення ",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Переглянути як текст (незавершено)",
|
||||
"Upload": "Завантажити (на сервер)",
|
||||
"Select upload location": "Виберіть місце для завантаження",
|
||||
"No upload locations set!": "Не встановлено місць для завантаження!",
|
||||
"Uploading": "Завантаження (на сервер)",
|
||||
"Upload successfull!": "Завантаження успішне!",
|
||||
"Upload failed!": "Помилка завантаження!",
|
||||
"Hash": "Хеш",
|
||||
"Hash Options": "Опції хешування",
|
||||
"Hashing": "Хешування",
|
||||
"Failed to hash file...": "Не вдалося обчислити хеш файлу...",
|
||||
"Ignore read only": "Ігнорувати лише читання",
|
||||
"Mount": "Монтувати",
|
||||
"Sd": "SD-карта",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "Запустити ",
|
||||
"Launch option for: ": "Опція запуску для: ",
|
||||
"Select launcher for: ": "Виберіть лаунчер для: ",
|
||||
"Close FileBrowser?": "Закрити файловий менеджер?",
|
||||
|
||||
"Sort By": "Сортувати за",
|
||||
"Sort Options": "Опції сортування",
|
||||
@@ -328,23 +357,23 @@
|
||||
"Ascending": "За зростанням",
|
||||
"Ascending (Up)": "За зростанням (вгору)",
|
||||
"Asc": "Зрост.",
|
||||
"Layout": "",
|
||||
"List": "",
|
||||
"Icon": "",
|
||||
"Grid": "",
|
||||
"Layout": "Макет",
|
||||
"List": "Список",
|
||||
"Icon": "Іконки",
|
||||
"Grid": "Сітка",
|
||||
"Search": "Пошук",
|
||||
|
||||
"Options": "Налаштування",
|
||||
"Split": "Розділити",
|
||||
"OK": "ОК",
|
||||
"Back": "Назад",
|
||||
"Select": "Вибрати",
|
||||
"Open": "Відкрити",
|
||||
"Close": "Закрити",
|
||||
"Launch": "Запустити",
|
||||
"Restart": "Перезапустити",
|
||||
"Next": "Наступний",
|
||||
"Prev": "Попередній",
|
||||
"Unstar": "Прибрати з обраного",
|
||||
"Star": "Позначити зіркою",
|
||||
"Yes": "Так",
|
||||
"No": "Ні",
|
||||
"On": "Увімк.",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Видалити",
|
||||
"Completely remove ": "Повністю видалити ",
|
||||
"Removing ": "Видалення ",
|
||||
"Removed ": "Видалено ",
|
||||
"Uninstalling ": "Видалення ",
|
||||
"Removed ": "Видалено ",
|
||||
|
||||
"Download": "Завантажити",
|
||||
"Downloading ": "Завантаження ",
|
||||
"Downloaded ": "Завантажено ",
|
||||
"Download via the Network options!": "Завантажуйте через опції мережі!",
|
||||
|
||||
"Update": "Оновити",
|
||||
"Update avaliable: ": "Доступне оновлення: ",
|
||||
@@ -380,13 +410,13 @@
|
||||
"Updated to ": "Оновлено до ",
|
||||
"Failed to download update": "Не вдалося завантажити оновлення",
|
||||
|
||||
"%zu hours %zu minutes remaining": "",
|
||||
"%zu minutes %zu seconds remaining": "",
|
||||
"%zu seconds remaining": "",
|
||||
"%zu hours %zu minutes remaining": "залишилось %zu год %zu хв",
|
||||
"%zu minutes %zu seconds remaining": "залишилось %zu хв %zu сек",
|
||||
"%zu seconds remaining": "залишилось %zu сек",
|
||||
|
||||
"Loading...": "Завантаження...",
|
||||
"Loading": "Завантаження",
|
||||
"Empty!": "Пусто!",
|
||||
"Not Ready...": "Не готово...",
|
||||
"Error loading page!": "Помилка завантаження сторінки!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"No Internet": "Không có Internet",
|
||||
"Switch-Handheld!": "Switch-Handheld!",
|
||||
"Switch-Docked!": "Switch-Docked!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "",
|
||||
"Audio disabled due to suspended game": "",
|
||||
"Are you sure you wish to cancel?": "Bạn có chắn muốn huỷ không?",
|
||||
"An error occurred": "",
|
||||
@@ -27,6 +28,8 @@
|
||||
"Nxlink Connected": "Nxlink Kết Nối",
|
||||
"Nxlink Upload": "Nxlink Đăng Tải",
|
||||
"Nxlink Finished": "Nxlink Hoàn Thành",
|
||||
"Hdd": "",
|
||||
"Hdd write protect": "",
|
||||
|
||||
"Language": "Ngôn ngữ",
|
||||
"Auto": "Tự động",
|
||||
@@ -57,22 +60,35 @@
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Dump options": "",
|
||||
"Dump Options": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Created nested folder": "",
|
||||
"Append folder with .xci": "",
|
||||
"Trim XCI": "",
|
||||
"Label trimmed XCI": "",
|
||||
"Multi-threaded USB transfer": "",
|
||||
"Dump All Bins": "",
|
||||
"Dump XCI": "",
|
||||
"Dump Card ID Set": "",
|
||||
"Dump Card UID": "",
|
||||
"Dump Certificate": "",
|
||||
"Dump Initial Data": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"microSD card (/dumps/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Success": "",
|
||||
|
||||
"Themezer": "Themezer",
|
||||
"Themezer Options": "Tuỳ chọn Themezer",
|
||||
@@ -86,9 +102,10 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "Đang tải json",
|
||||
"Select asset to download for ": "Chọn nội dung để tải xuống cho ",
|
||||
"Failed to download json": "",
|
||||
"Failed to download app!": "",
|
||||
|
||||
"FTP Install": "",
|
||||
"FTP Install (EXPERIMENTAL)": "",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: Ethernet": "",
|
||||
"Connection Type: None": "",
|
||||
@@ -98,9 +115,9 @@
|
||||
"Password:": "",
|
||||
"SSID:": "",
|
||||
"Passphrase:": "",
|
||||
"Failed to install via FTP, press B to exit...": "",
|
||||
"Ftp install success!": "",
|
||||
"Ftp install failed!": "",
|
||||
"Failed to install, press B to exit...": "",
|
||||
"Install success!": "",
|
||||
"Install failed!": "",
|
||||
"USB Install": "",
|
||||
"USB": "",
|
||||
"Connected, waiting for file list...": "",
|
||||
@@ -122,9 +139,12 @@
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "",
|
||||
"No GameCard inserted": "",
|
||||
"GameCard is already trimmed!": "",
|
||||
"WARNING: GameCard is already trimmed!": "",
|
||||
"Continue": "",
|
||||
"Gc install success!": "",
|
||||
"Gc install failed!": "",
|
||||
|
||||
@@ -160,14 +180,14 @@
|
||||
"Negative image": "Ảnh âm bản",
|
||||
"Format": "Định dạng",
|
||||
"Trimming Format": "Định dạng cắt tỉa",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
"40x30": "40×30",
|
||||
"20x15": "20×15",
|
||||
"External Light Filter": "Bộ lộc ánh sáng bên ngoài",
|
||||
"Load Default": "Tải mặc định",
|
||||
|
||||
"Web": "",
|
||||
"Select URL": "",
|
||||
"Enter custom URL": "",
|
||||
"Enter URL": "",
|
||||
|
||||
"Advanced": "Mở rộng",
|
||||
"Advanced Options": "Tuỳ chọn mở rộng",
|
||||
"Logging": "Logging",
|
||||
@@ -181,10 +201,12 @@
|
||||
"Restored hbmenu": "Đã khôi phục hbmenu",
|
||||
"Restart Sphaira?": "Khởi động lại Sphaira?",
|
||||
"Press OK to restart Sphaira": "",
|
||||
"Boost CPU during transfer": "",
|
||||
"Text scroll speed": "",
|
||||
"Slow": "",
|
||||
"Normal": "",
|
||||
"Fast": "",
|
||||
"Set left-side menu": "",
|
||||
"Set right-side menu": "",
|
||||
"Install options": "",
|
||||
"Install Options": "",
|
||||
@@ -194,7 +216,6 @@
|
||||
"Install location": "Vị trí cài đặt",
|
||||
"System memory": "Bộ nhớ máy",
|
||||
"microSD card": "Thẻ nhớ",
|
||||
"Boost CPU clock": "",
|
||||
"Allow downgrade": "",
|
||||
"Skip if already installed": "",
|
||||
"Ticket only": "",
|
||||
@@ -222,9 +243,11 @@
|
||||
"Creating Control": "Tạo điều khiển",
|
||||
"Creating Meta": "Tạo Meta",
|
||||
"Writing Nca": "Ghi Nca",
|
||||
"Updating ncm databse": "Cập nhật ncm databse",
|
||||
"Updating ncm database": "Cập nhật ncm database",
|
||||
"Pushing application record": "Đẩy ứng dụng",
|
||||
"Failed to install forwarder": "Cài đặt ra ngoài màn hình thất bại",
|
||||
"Unstar": "Xoá yêu thích",
|
||||
"Star": "Yêu thích",
|
||||
"Unstarred ": "Bỏ yêu thích ",
|
||||
"Starred ": "Đã yêu thích ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
@@ -257,12 +280,13 @@
|
||||
"Copy": "Sao chép",
|
||||
"Copying ": "Đang sao chép ",
|
||||
"Paste": "Dán",
|
||||
"Paste ": "Paste ",
|
||||
" file(s)?": " tập tin(nhiều)?",
|
||||
"Paste file(s)?": "",
|
||||
"Pasting ": "Đang dán ",
|
||||
"Pasting": "Đang dán",
|
||||
"Rename": "Đổi tên",
|
||||
"Set New File Name": "Đặt tên mới cho tập tin",
|
||||
"Failed to delete directory": "",
|
||||
"Failed to delete file": "",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
@@ -285,13 +309,17 @@
|
||||
"Create Folder": "Tạo thư mục",
|
||||
"Set Folder Name": "Đặt tên thư mục",
|
||||
"Creating ": "Đang tạo ",
|
||||
"View as text (unfinished)": "Xem dạng văn bản (chưa xong)",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "Xem dạng văn bản (chưa xong)",
|
||||
"Hash": "",
|
||||
"Hash Options": "",
|
||||
"Hashing": "",
|
||||
"Failed to hash file...": "",
|
||||
"Ignore read only": "Bỏ qua chỉ đọc",
|
||||
"Mount": "Gắn",
|
||||
"Sd": "Sd",
|
||||
@@ -302,6 +330,7 @@
|
||||
"Launch ": "Chạy ",
|
||||
"Launch option for: ": "Chạy với tuỳ chọn cho: ",
|
||||
"Select launcher for: ": "Chọn trình chạy cho: ",
|
||||
"Close FileBrowser?": "",
|
||||
|
||||
"Sort By": "Sắp xếp bởi",
|
||||
"Sort Options": "Tuỳ chọn sắp xếp",
|
||||
@@ -335,16 +364,16 @@
|
||||
"Search": "Tìm kiếm",
|
||||
|
||||
"Options": "Tuỳ chọn",
|
||||
"Split": "",
|
||||
"OK": "OK",
|
||||
"Back": "Trở về",
|
||||
"Select": "Chọn",
|
||||
"Open": "Mở",
|
||||
"Close": "",
|
||||
"Launch": "Chạy",
|
||||
"Restart": "Khởi động lại",
|
||||
"Next": "",
|
||||
"Prev": "",
|
||||
"Unstar": "Xoá yêu thích",
|
||||
"Star": "Yêu thích",
|
||||
"Yes": "Có",
|
||||
"No": "Không",
|
||||
"On": "",
|
||||
@@ -367,12 +396,13 @@
|
||||
"Remove": "Gỡ",
|
||||
"Completely remove ": "Đã gỡ thành công ",
|
||||
"Removing ": "Đang gỡ ",
|
||||
"Removed ": "Đã gỡ ",
|
||||
"Uninstalling ": "Đang gỡ cài đặt ",
|
||||
"Removed ": "Đã gỡ ",
|
||||
|
||||
"Download": "Tải về",
|
||||
"Downloading ": "Đang tải xuống ",
|
||||
"Downloaded ": "Đã tải xong ",
|
||||
"Download via the Network options!": "",
|
||||
|
||||
"Update": "Cập nhật",
|
||||
"Update avaliable: ": "Cập nhậc có sẵn: ",
|
||||
@@ -389,4 +419,4 @@
|
||||
"Empty!": "Trống!",
|
||||
"Not Ready...": "Chưa sẵn sàng...",
|
||||
"Error loading page!": "Lỗi tải trang!"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
"No Internet": "网络未连接",
|
||||
"Switch-Handheld!": "切换至掌机模式!",
|
||||
"Switch-Docked!": "切换至底座模式!",
|
||||
"Warning! Logs are enabled, Sphaira will run slowly!": "警告!日志已启用,将导致Sphaira运行缓慢!",
|
||||
"Audio disabled due to suspended game": "由于游戏暂停,音频已禁用",
|
||||
"Are you sure you wish to cancel?": "您确定要取消吗?",
|
||||
"An error occurred": "",
|
||||
"An error occurred": "发生错误",
|
||||
"If this message appears repeatedly, please open an issue.": "若此消息反复出现,请提交问题报告。",
|
||||
|
||||
"Menu Options": "菜单选项",
|
||||
@@ -16,17 +17,20 @@
|
||||
"Music": "音乐",
|
||||
"12 Hour Time": "12小时制时间",
|
||||
"Download Default Music": "下载默认音乐",
|
||||
"Failed to download default_music.bfstm, please try again": "",
|
||||
"Overwrite current default music?": "",
|
||||
"Failed to download default_music.bfstm, please try again": "下载default_music.bfstm失败,请重试",
|
||||
"Overwrite current default music?": "覆盖当前默认音乐?",
|
||||
|
||||
"Network": "网络",
|
||||
"Network Options": "网络选项",
|
||||
"Ftp": "FTP",
|
||||
"Mtp": "MTP",
|
||||
"Nxlink": "Nxlink插件提交",
|
||||
"Nxlink Connected": "Nxlink 已连接",
|
||||
"Nxlink Upload": "Nxlink 上传中",
|
||||
"Nxlink Finished": "Nxlink 已结束",
|
||||
"MTP Install": "MTP安装",
|
||||
"Nxlink": "Nxlink上传",
|
||||
"Nxlink Connected": "Nxlink已连接",
|
||||
"Nxlink Upload": "Nxlink上传中",
|
||||
"Nxlink Finished": "Nxlink已结束",
|
||||
"Hdd": "HDD",
|
||||
"Hdd write protect": "HDD写保护",
|
||||
|
||||
"Language": "语言",
|
||||
"Auto": "自动",
|
||||
@@ -46,33 +50,66 @@
|
||||
"Ukrainian": "Українська",
|
||||
|
||||
"Misc": "拓展",
|
||||
"Misc Options": "拓展设置",
|
||||
"Misc Options": "拓展选项",
|
||||
"Games": "游戏",
|
||||
"Game Options": "游戏选项",
|
||||
"Hide forwarders": "隐藏前端启动",
|
||||
"Launch random game": "开启随机游戏",
|
||||
"Launch random game": "启动随机游戏",
|
||||
"List meta records": "列出元数据记录",
|
||||
"Entries": "条目",
|
||||
"Failed to list application meta entries": "",
|
||||
"No meta entries found...\n": "",
|
||||
"Updating application record list": "",
|
||||
"Dump": "",
|
||||
"Select content to dump": "",
|
||||
"Dump All": "",
|
||||
"Dump Application": "",
|
||||
"Dump Patch": "",
|
||||
"Dump AddOnContent": "",
|
||||
"Dump DataPatch": "",
|
||||
"Select dump location": "",
|
||||
"microSD card (/dumps/NSP/)": "",
|
||||
"USB transfer (Switch 2 Switch)": "",
|
||||
"/dev/null (Speed Test)": "",
|
||||
"Dumping": "",
|
||||
"Dump successfull!": "",
|
||||
"Dump failed!": "",
|
||||
"Success": "",
|
||||
"Delete successfull!": "",
|
||||
"Delete failed!": "",
|
||||
"Failed to list application meta entries": "未能列出应用程序元条目",
|
||||
"No meta entries found...\n": "未找到元条目...\n",
|
||||
"Updating application record list": "更新应用程序记录列表",
|
||||
"Dump": "转储",
|
||||
"Select content to dump": "选择要转储的内容",
|
||||
"Dump All": "转储全部内容",
|
||||
"Dump Application": "转储应用程序(本体)",
|
||||
"Dump Patch": "转储更新补丁(UPD)",
|
||||
"Dump AddOnContent": "转储追加内容(DLC)",
|
||||
"Dump DataPatch": "转储数据补丁",
|
||||
"Dump All Bins": "转储全部文件",
|
||||
"Dump XCI": "转储.xci",
|
||||
"Dump Card ID Set": "转储游戏卡ID",
|
||||
"Dump Card UID": "转储游戏卡UID",
|
||||
"Dump Certificate": "转储游戏证书",
|
||||
"Dump Initial Data": "转储初始数据",
|
||||
"Select dump location": "选择转储位置",
|
||||
"microSD card (/dumps/NSP/)": "microSD卡(/dumps/NSP/)",
|
||||
"USB transfer (Switch 2 Switch)": "USB传输(Switch对Switch)",
|
||||
"/dev/null (Speed Test)": "/dev/null(速度测试)",
|
||||
"Dumping": "正在转储",
|
||||
"Dump successfull!": "转储成功!",
|
||||
"Dump failed!": "转储失败!",
|
||||
"Success": "成功",
|
||||
"Delete successfull!": "删除成功!",
|
||||
"Delete failed!": "删除失败!",
|
||||
"Title cache": "标题缓存",
|
||||
|
||||
"Saves": "存档",
|
||||
"Save Options": "存档选项",
|
||||
"Account": "账户",
|
||||
"Backup": "备份",
|
||||
"Auto backup": "自动备份",
|
||||
"Auto backup on restore": "还原时自动备份",
|
||||
"Compress backup": "压缩备份",
|
||||
"Are you sure you want to backup save(s)?": "您确定要备份存档吗?",
|
||||
"No saves found in ": "未找到存档 ",
|
||||
"Backed up to ": "已备份到 ",
|
||||
"Backup successfull!": "已备份成功!",
|
||||
"Backup failed!": "备份失败!",
|
||||
"Restore save for: ": "还原备份文件: ",
|
||||
"Are you sure you want to restore ": "您确定要还原已备份文件吗 ",
|
||||
"Restore successfull!": "已还原成功!",
|
||||
"Restore failed!": "还原失败!",
|
||||
"Data Type": "数据类型",
|
||||
"System": "系统",
|
||||
"BCAT": "BCAT数据",
|
||||
"Device": "设备",
|
||||
"Temporary": "临时",
|
||||
"Cache": "缓存",
|
||||
"System BCAT": "系统备份",
|
||||
"Select backup location": "选择备份位置",
|
||||
"Select restore location": "选择还原位置",
|
||||
|
||||
"Themezer": "在线主题",
|
||||
"Themezer Options": "在线主题选项",
|
||||
@@ -86,10 +123,11 @@
|
||||
"GitHub": "GitHub",
|
||||
"Downloading json": "正在下载 json",
|
||||
"Select asset to download for ": "选择要下载的资源用于 ",
|
||||
"Failed to download json": "下载json失败",
|
||||
"Failed to download app!": "下载应用程序失败!",
|
||||
|
||||
"FTP Install": "通过 FTP 安装",
|
||||
"FTP Install (EXPERIMENTAL)": "通过 FTP 安装(实验性)",
|
||||
"Connection Type: WiFi | Strength: ": "",
|
||||
"Connection Type: WiFi | Strength: ": "连接类型:WiFi |强度:",
|
||||
"Connection Type: Ethernet": "连接类型:以太网",
|
||||
"Connection Type: None": "连接类型:无",
|
||||
"Host:": "主机:",
|
||||
@@ -98,45 +136,64 @@
|
||||
"Password:": "密码:",
|
||||
"SSID:": "网络名称:",
|
||||
"Passphrase:": "密码:",
|
||||
"Failed to install via FTP, press B to exit...": "通过 FTP 安装失败,按 B 键退出...",
|
||||
"Ftp install success!": "通过 FTP 安装成功。",
|
||||
"Ftp install failed!": "通过 FTP 安装失败。",
|
||||
"Failed to install, press B to exit...": "通过 FTP 安装失败,按 B 键退出...",
|
||||
"Install success!": "通过 FTP 安装成功。",
|
||||
"Install failed!": "通过 FTP 安装失败。",
|
||||
"USB Install": "通过 USB 安装",
|
||||
"USB": "USB",
|
||||
"Connected, waiting for file list...": "",
|
||||
"Connected, starting transfer...": "",
|
||||
"Connected, waiting for file list...": "已连接,正在等待文件列表...",
|
||||
"Connected, starting transfer...": "已连接,开始传输...",
|
||||
"Failed to init usb, press B to exit...": "USB 初始化失败,按 B 键退出...",
|
||||
"Waiting for connection...": "等待连接中...",
|
||||
"Transferring data...": "正在传输数据...",
|
||||
"USB connected, sending file list": "",
|
||||
"Sent file list, waiting for command...": "",
|
||||
"waiting for usb connection...": "",
|
||||
"USB connected, sending file list": "USB已连接,正在发送文件列表",
|
||||
"Sent file list, waiting for command...": "已发送文件列表,正在等待命令...",
|
||||
"waiting for usb connection...": "等待USB连接...",
|
||||
"Disable MTP for usb install": "暂时禁用 USB 安装的 MTP 功能",
|
||||
"Re-enabled MTP": "重新启用 MTP",
|
||||
"Installed via usb": "通过 USB 安装",
|
||||
"Usb install success!": "通过 USB 安装成功。",
|
||||
"Usb install failed!": "通过 USB 安装失败。",
|
||||
"Usb install success!": "USB安装成功!",
|
||||
"Usb install failed!": "USB安装失败!",
|
||||
"State: %s | Speed: %s": "状态: %s | 速度: %s",
|
||||
"Detached": "已断开",
|
||||
"Attached": "已连接",
|
||||
"Powered": "已供电",
|
||||
"Default": "默认",
|
||||
"Address": "地址",
|
||||
"Configured": "已配置",
|
||||
"Suspended": "已挂起",
|
||||
"USB 1.0 Low Speed": "USB 1.0 低速",
|
||||
"USB 1.1 Full Speed": "USB 1.1 全速",
|
||||
"USB 2.0 High Speed": "USB 2.0 高速",
|
||||
"USB 3.0 Super Speed": "USB 3.0 超高速",
|
||||
"Drag'n'Drop (NSP, XCI, NSZ, XCZ) to the install folder": "拖放文件(NSP, XCI, NSZ, XCZ)至安装文件夹",
|
||||
"Failed to install via MTP, press B to exit...": "MTP安装失败,请按B键退出...",
|
||||
"MTP install success!": "MTP安装成功!",
|
||||
"MTP install failed!": "MTP安装失败!",
|
||||
"Press B to exit...": "按 B 键退出...",
|
||||
"GameCard Install": "卡带安装",
|
||||
"GameCard": "卡带",
|
||||
"GC": "",
|
||||
"System memory %.1f GB": "",
|
||||
"microSD card %.1f GB": "",
|
||||
"Nand Install": "",
|
||||
"SD Card Install": "",
|
||||
"Exit": "",
|
||||
"GameCard Install": "游戏卡安装",
|
||||
"GameCard": "游戏卡",
|
||||
"GC": "GC",
|
||||
"System memory %.1f GB": "主机内存 %.1f GB",
|
||||
"microSD card %.1f GB": "microSD卡 %.1f GB",
|
||||
"Exit": "退出",
|
||||
"Install disabled...\nPlease enable installing via the install options.": "安装已禁用...\n请通过安装选项启用安装。",
|
||||
"No GameCard inserted": "未插入游戏卡",
|
||||
"GameCard is already trimmed!": "游戏卡已被修剪!",
|
||||
"WARNING: GameCard is already trimmed!": "警告:游戏卡已被修剪!",
|
||||
"Continue": "继续",
|
||||
"Gc install success!": "游戏安装成功。",
|
||||
"Gc install failed!": "游戏安装失败。",
|
||||
|
||||
"IRS (Infrared Joycon Camera)": "",
|
||||
"IRS": "",
|
||||
"IRS (Infrared Joycon Camera)": "IRS(Joycon红外摄像头)",
|
||||
"IRS": "IRS",
|
||||
"Irs": "红外成像",
|
||||
"Ambient Noise Level: ": "环境噪声等级:",
|
||||
"Controller": "控制器",
|
||||
"Pad ": "手柄 ",
|
||||
"HandHeld": "掌机模式",
|
||||
" (Available)": " (可用的)",
|
||||
" (Unsupported)": " (不支持的)",
|
||||
" (Available)": " (可用)",
|
||||
" (Unsupported)": " (不支持)",
|
||||
" (Unconnected)": " (未连接)",
|
||||
"Rotation": "旋转",
|
||||
"0 (Sideways)": "0度",
|
||||
@@ -159,7 +216,7 @@
|
||||
"Normal image": "正常图像",
|
||||
"Negative image": "负片图像",
|
||||
"Format": "格式",
|
||||
"Trimming Format": "裁剪格式",
|
||||
"Trimming Format": "修剪格式",
|
||||
"320x240": "320×240",
|
||||
"160x120": "160×120",
|
||||
"80x60": "80×60",
|
||||
@@ -168,6 +225,11 @@
|
||||
"External Light Filter": "外部光滤镜",
|
||||
"Load Default": "加载默认值",
|
||||
|
||||
"Web": "网络",
|
||||
"Select URL": "选择链接",
|
||||
"Enter custom URL": "输入自定义链接",
|
||||
"Enter URL": "输入网址",
|
||||
|
||||
"Advanced": "高级",
|
||||
"Advanced Options": "高级选项",
|
||||
"Logging": "日志",
|
||||
@@ -181,15 +243,17 @@
|
||||
"Restored hbmenu": "已恢复 hbmenu",
|
||||
"Restart Sphaira?": "重启 Sphaira?",
|
||||
"Press OK to restart Sphaira": "按OK键以重启shphaira菜单",
|
||||
"Boost CPU during transfer": "传输时提升CPU频率",
|
||||
"Text scroll speed": "文本滚动速度",
|
||||
"Slow": "慢",
|
||||
"Normal": "正常",
|
||||
"Fast": "快",
|
||||
"Set right-side menu": "",
|
||||
"Set left-side menu": "设置左侧菜单",
|
||||
"Set right-side menu": "设置右侧菜单",
|
||||
"Install options": "安装选项",
|
||||
"Install Options": "安装选项",
|
||||
"Enable sysmmc": "",
|
||||
"Enable emummc": "",
|
||||
"Enable sysmmc": "启用sysmmc",
|
||||
"Enable emummc": "启用emummc",
|
||||
"Show install warning": "显示安装警告",
|
||||
"Install location": "安装位置",
|
||||
"System memory": "主机内存",
|
||||
@@ -199,21 +263,28 @@
|
||||
"Skip if already installed": "若已安装则跳过",
|
||||
"Ticket only": "仅安装票据",
|
||||
"Skip base": "跳过基础部分",
|
||||
"Skip patch": "跳过补丁",
|
||||
"Skip dlc": "跳过 DLC(可下载内容)",
|
||||
"Skip patch": "跳过更新补丁(UPD)",
|
||||
"Skip dlc": "跳过追加内容(DLC)",
|
||||
"Skip data patch": "跳过数据补丁",
|
||||
"Skip ticket": "跳过票据",
|
||||
"Skip NCA hash verify": "",
|
||||
"Skip NCA hash verify": "跳过 NCA 哈希验证",
|
||||
"Skip RSA header verify": "跳过 RSA 头部验证",
|
||||
"Skip RSA NPDM verify": "跳过 RSA NPDM 验证",
|
||||
"Ignore distribution bit": "忽略分布位",
|
||||
"Convert to standard crypto": "转换为标准加密方式",
|
||||
"Lower master key": "降低主密钥",
|
||||
"Lower system version": "降低系统版本",
|
||||
"Dump options": "转储选项",
|
||||
"Dump Options": "转储选项",
|
||||
"Created nested folder": "已创建嵌套文件夹",
|
||||
"Append folder with .xci": "用.xci附加文件夹",
|
||||
"Trim XCI": "裁剪 XCI",
|
||||
"Label trimmed XCI": "标记已裁剪的XCI",
|
||||
"Multi-threaded USB transfer": "多线程USB传输",
|
||||
|
||||
"Homebrew": "应用列表",
|
||||
"Homebrew": "自制软件",
|
||||
"Apps": "应用",
|
||||
"Homebrew Options": "应用选项",
|
||||
"Homebrew Options": "自制软件选项",
|
||||
"Hide Sphaira": "在应用列表中隐藏Sphaira",
|
||||
"Install Forwarder": "安装前端应用",
|
||||
"WARNING: Installing forwarders will lead to a ban!": "警告:安装前端应用可能导致ban机!",
|
||||
@@ -222,15 +293,17 @@
|
||||
"Creating Control": "正在创建控制器",
|
||||
"Creating Meta": "正在创建元数据",
|
||||
"Writing Nca": "正在写入Nca",
|
||||
"Updating ncm databse": "正在更新ncm数据库",
|
||||
"Updating ncm database": "正在更新ncm数据库",
|
||||
"Pushing application record": "正在推送应用记录",
|
||||
"Failed to install forwarder": "前端应用安装失败",
|
||||
"Unstar": "取消星标",
|
||||
"Star": "星标",
|
||||
"Unstarred ": "取消星标 ",
|
||||
"Starred ": "已星标 ",
|
||||
"Failed to remove old forwarder, please manually remove it!": "",
|
||||
"Failed to remove old forwarder, please manually remove it!": "删除旧前端应用失败,请手动删除!",
|
||||
|
||||
"AppStore": "应用商店",
|
||||
"Appstore": "",
|
||||
"Appstore": "应用商店",
|
||||
"Store": "商店",
|
||||
"Filter: %s | Sort: %s | Order: %s": "筛选: %s | 排序: %s | 顺序: %s",
|
||||
"AppStore Options": "应用商店选项",
|
||||
@@ -245,7 +318,7 @@
|
||||
"More by Author": "作者更多作品",
|
||||
"Leave Feedback": "留言反馈",
|
||||
|
||||
"FileBrowser": "文件浏览",
|
||||
"FileBrowser": "文件浏览器",
|
||||
"Files": "文件",
|
||||
"%zd files": "%zd 个文件",
|
||||
"%zd dirs": "%zd 个文件夹",
|
||||
@@ -257,41 +330,46 @@
|
||||
"Copy": "复制",
|
||||
"Copying ": "正在复制 ",
|
||||
"Paste": "粘贴",
|
||||
"Paste ": "粘贴 ",
|
||||
" file(s)?": "个文件(夹)?",
|
||||
"Paste file(s)?": "粘贴 个文件(夹)?",
|
||||
"Pasting ": "正在粘贴 ",
|
||||
"Pasting": "正在粘贴",
|
||||
"Rename": "重命名",
|
||||
"Set New File Name": "输入新命名",
|
||||
"Extract zip": "",
|
||||
"Extract Options": "",
|
||||
"Extract here": "",
|
||||
"Extract to root": "",
|
||||
"Are you sure you want to extract to root?": "",
|
||||
"Extract to...": "",
|
||||
"Enter the path to the folder to extract into": "",
|
||||
"Extracting ": "",
|
||||
"Extract success!": "",
|
||||
"Extract failed!": "",
|
||||
"Failed to delete directory": "删除目录失败",
|
||||
"Failed to delete file": "删除文件失败",
|
||||
"Extract zip": "解压 zip",
|
||||
"Extract Options": "解压选项",
|
||||
"Extract here": "解压到这里",
|
||||
"Extract to root": "解压到根目录",
|
||||
"Are you sure you want to extract to root?": "您确定要解压到根目录吗?",
|
||||
"Extract to...": "解压到...",
|
||||
"Enter the path to the folder to extract into": "输入要解压到的文件夹的路径",
|
||||
"Extracting ": "正在解压",
|
||||
"Extract success!": "解压成功!",
|
||||
"Extract failed!": "解压失败!",
|
||||
"Compress to zip": "压缩到zip",
|
||||
"Compress Options": "",
|
||||
"Compress": "",
|
||||
"Compress to...": "",
|
||||
"Compressing ": "",
|
||||
"Compress success!": "",
|
||||
"Compress failed!": "",
|
||||
"Compress Options": "压缩选项",
|
||||
"Compress": "压缩",
|
||||
"Compress to...": "压缩到...",
|
||||
"Compressing ": "正在压缩 ",
|
||||
"Compress success!": "压缩成功!",
|
||||
"Compress failed!": "压缩失败",
|
||||
"Create File": "新建文件",
|
||||
"Set File Name": "输入文件名",
|
||||
"Create Folder": "新建文件夹",
|
||||
"Set Folder Name": "输入文件夹名",
|
||||
"Creating ": "正在创建 ",
|
||||
"Upload": "",
|
||||
"Select upload location": "",
|
||||
"No upload locations set!": "",
|
||||
"Uploading": "",
|
||||
"Upload successfull!": "",
|
||||
"Upload failed!": "",
|
||||
"View as text (unfinished)": "以文本形式查看(未完善)",
|
||||
"Upload": "上传",
|
||||
"Select upload location": "选择上传位置",
|
||||
"No upload locations set!": "未设置上传位置",
|
||||
"Uploading": "正在上传",
|
||||
"Upload successfull!": "上传成功!",
|
||||
"Upload failed!": "上传失败!",
|
||||
"Hash": "哈希",
|
||||
"Hash Options": "哈希选项",
|
||||
"Hashing": "正在计算文件哈希",
|
||||
"Failed to hash file...": "计算文件哈希失败...",
|
||||
"Ignore read only": "忽略只读",
|
||||
"Mount": "挂载",
|
||||
"Sd": "SD卡",
|
||||
@@ -302,6 +380,7 @@
|
||||
"Launch ": "启动 ",
|
||||
"Launch option for: ": "启动选项:",
|
||||
"Select launcher for: ": "选择启动器用于:",
|
||||
"Close FileBrowser?": "是否关闭文件浏览器?",
|
||||
|
||||
"Sort By": "排序方式",
|
||||
"Sort Options": "排序选项",
|
||||
@@ -310,7 +389,7 @@
|
||||
"Emulators": "模拟器",
|
||||
"Tools": "工具",
|
||||
"Themes": "主题",
|
||||
"Legacy": "可更新",
|
||||
"Legacy": "旧版",
|
||||
"Sort": "排序",
|
||||
"Size": "按大小",
|
||||
"Size (Star)": "按大小(星标优先)",
|
||||
@@ -328,32 +407,32 @@
|
||||
"Ascending": "升序",
|
||||
"Ascending (Up)": "升序",
|
||||
"Asc": "升序",
|
||||
"Layout": "",
|
||||
"List": "",
|
||||
"Icon": "",
|
||||
"Grid": "",
|
||||
"Layout": "布局",
|
||||
"List": "列表",
|
||||
"Icon": "图标",
|
||||
"Grid": "网格",
|
||||
"Search": "搜索",
|
||||
|
||||
"Options": "选项",
|
||||
"Split": "拆分",
|
||||
"OK": "确定",
|
||||
"Back": "返回",
|
||||
"Select": "选择",
|
||||
"Open": "打开",
|
||||
"Close": "关闭",
|
||||
"Launch": "启动",
|
||||
"Restart": "重启",
|
||||
"Next": "下一项",
|
||||
"Prev": "上一项",
|
||||
"Unstar": "取消星标",
|
||||
"Star": "星标",
|
||||
"Yes": "是",
|
||||
"No": "否",
|
||||
"On": "",
|
||||
"Off": "",
|
||||
"On": "开启",
|
||||
"Off": "关闭",
|
||||
|
||||
"Install": "安装",
|
||||
"Install Selected files?": "安装所选文件?",
|
||||
"Installing ": "正在安装 ",
|
||||
"Installed ": "",
|
||||
"Installed ": "已安装",
|
||||
"Installed!": "安装完成!",
|
||||
"Trying to load ": "尝试加载 ",
|
||||
"Checking MD5": "正在校验 MD5",
|
||||
@@ -367,12 +446,13 @@
|
||||
"Remove": "删除",
|
||||
"Completely remove ": "彻底删除 ",
|
||||
"Removing ": "正在移除 ",
|
||||
"Removed ": "已移除 ",
|
||||
"Uninstalling ": "正在卸载 ",
|
||||
"Removed ": "已移除 ",
|
||||
|
||||
"Download": "下载",
|
||||
"Downloading ": "正在下载 ",
|
||||
"Downloaded ": "已下载 ",
|
||||
"Download via the Network options!": "通过网络选项下载!",
|
||||
|
||||
"Update": "更新",
|
||||
"Update avaliable: ": "有可用更新!",
|
||||
@@ -380,13 +460,35 @@
|
||||
"Updated to ": "更新至 ",
|
||||
"Failed to download update": "更新下载失败",
|
||||
|
||||
"%zu hours %zu minutes remaining": "",
|
||||
"%zu minutes %zu seconds remaining": "",
|
||||
"%zu seconds remaining": "",
|
||||
"%zu hours %zu minutes remaining": "剩余 %zu 小时 %zu 分钟",
|
||||
"%zu minutes %zu seconds remaining": "剩余 %zu 分钟 %zu 秒",
|
||||
"%zu seconds remaining": "剩余 %zu 秒",
|
||||
|
||||
"Loading...": "加载中...",
|
||||
"Loading": "加载中",
|
||||
"Empty!": "空空如野!",
|
||||
"Empty!": "空空如也!",
|
||||
"Not Ready...": "尚未准备好...",
|
||||
"Error loading page!": "页面加载失败!"
|
||||
}
|
||||
"Error loading page!": "页面加载失败!",
|
||||
"USB Speed Status": "USB速度状态",
|
||||
"Data Type": "数据类型",
|
||||
"Save Location Selection": "存档位置选择",
|
||||
"MTP Transfer": "MTP传输",
|
||||
"USB Installation": "USB安装",
|
||||
"Archive Data": "存档数据",
|
||||
"Game Card Data": "游戏卡数据",
|
||||
"Storage Path": "存储路径",
|
||||
"MTP Connection": "MTP连接",
|
||||
"USB Device": "USB设备",
|
||||
"Data Category": "数据类别",
|
||||
"Save Management": "存档管理",
|
||||
"Transfer Mode": "传输模式",
|
||||
"Installation Method": "安装方式",
|
||||
"File Type": "文件类型",
|
||||
"Storage Device": "存储设备",
|
||||
"Data Transfer": "数据传输",
|
||||
"Save Data Type": "存档数据类型",
|
||||
"USB Connection": "USB连接",
|
||||
"MTP Settings": "MTP设置",
|
||||
"USB Configuration": "USB配置",
|
||||
"Data Storage": "数据存储"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ background = 0x2d2d2d
|
||||
grid = 0x46464630
|
||||
popup = 0x2d2d2d
|
||||
error = 0xfa5a3a
|
||||
focus = 0x000000b4
|
||||
|
||||
line = 0xfbfbfb
|
||||
line_separator = 0x707070
|
||||
|
||||
@@ -3,6 +3,7 @@ background = 0xebebeb
|
||||
grid = 0xf0f0f0
|
||||
popup = 0xebebeb
|
||||
error = 0xfa5a3a
|
||||
focus = 0xd3d3d3a0
|
||||
|
||||
line = 0x373737
|
||||
line_separator = 0x6d787a
|
||||
|
||||
30
assets/romfs/themes/default_theme.ini
Normal file
@@ -0,0 +1,30 @@
|
||||
[meta]
|
||||
name=Default
|
||||
author=ninstar
|
||||
version=1.0.0
|
||||
inherit=romfs:/themes/base_black_theme.ini
|
||||
|
||||
[theme]
|
||||
background = 0x111f28
|
||||
grid = 0x122430
|
||||
popup = 0x143144
|
||||
error = 0xfa3a5d
|
||||
|
||||
line = 0x335e77
|
||||
line_separator = 0x163951
|
||||
|
||||
text = 0xfbfbfb
|
||||
text_info = 0xbed0d6
|
||||
text_selected = 0x32ffcf
|
||||
selected_background = 0x143144
|
||||
|
||||
sidebar = 0x071013ef
|
||||
|
||||
scrollbar = 0x32ffcf
|
||||
scrollbar_background = ; hide the background
|
||||
|
||||
progressbar = 0x32ffcf
|
||||
progressbar_background = 0x0B1519
|
||||
|
||||
highlight_1 = 0x69ff8f
|
||||
highlight_2 = 0x5cbeff
|
||||
|
Before Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 116 KiB |
BIN
assets/screenshots/appstore.jpg
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
assets/screenshots/appstore_page.jpg
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
assets/screenshots/file_browser.jpg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
assets/screenshots/games.jpg
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
assets/screenshots/homebrew.jpg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
assets/screenshots/launch_options.jpg
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
assets/screenshots/themezer.jpg
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
assets/screenshots/web.jpg
Normal file
|
After Width: | Height: | Size: 100 KiB |
@@ -8,13 +8,13 @@ build_preset() {
|
||||
cmake --build --preset $1
|
||||
}
|
||||
|
||||
build_preset MinSizeRel
|
||||
build_preset Release
|
||||
|
||||
rm -rf out
|
||||
|
||||
# --- SWITCH --- #
|
||||
mkdir -p out/switch/sphaira/
|
||||
cp -r build/MinSizeRel/*.nro out/switch/sphaira/sphaira.nro
|
||||
cp -r build/Release/*.nro out/switch/sphaira/sphaira.nro
|
||||
pushd out
|
||||
zip -r9 sphaira.zip switch
|
||||
popd
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define EXIT_DETECTION_STR "if this isn't replaced i will exit :)"
|
||||
|
||||
// this uses 3-4KiB more than nx-hbloader.
|
||||
// this uses 16KiB less than nx-hbloader.
|
||||
static char g_argv[2048] = {0};
|
||||
// this can and will be modified by libnx if launched nro calls envSetNextLoad().
|
||||
static char g_nextArgv[2048] = {0};
|
||||
@@ -47,7 +46,7 @@ static void fix_nro_path(char* path) {
|
||||
|
||||
// Credit to behemoth
|
||||
// SOURCE: https://github.com/HookedBehemoth/nx-hbloader/commit/7f8000a41bc5e8a6ad96a097ef56634cfd2fabcb
|
||||
static NX_NORETURN void selfExit(void) {
|
||||
static void NX_NORETURN selfExit(void) {
|
||||
Result rc = smInitialize();
|
||||
if (R_FAILED(rc))
|
||||
goto fail0;
|
||||
@@ -162,7 +161,8 @@ static void getOwnProcessHandle(void) {
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 12));
|
||||
|
||||
Thread t;
|
||||
rc = threadCreate(&t, &procHandleReceiveThread, (void*)(uintptr_t)server_handle, NULL, 0x1000, 0x20, 0);
|
||||
u8* stack = g_heapAddr;
|
||||
rc = threadCreate(&t, &procHandleReceiveThread, (void*)(uintptr_t)server_handle, stack, 0x1000, 0x20, 0);
|
||||
if (R_FAILED(rc))
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 10));
|
||||
|
||||
@@ -290,10 +290,10 @@ void NX_NORETURN loadNro(void) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
|
||||
u8* romfs_dirs = malloc(romfs_header.dirTableSize); // should be 1 entry ("/")
|
||||
u8* romfs_files = malloc(romfs_header.fileTableSize); // should be 2 entries (argv and nro)
|
||||
u8 romfs_dirs[1024 * 2]; // should be 1 entry ("/")
|
||||
u8 romfs_files[1024 * 4]; // should be 2 entries (argv and nro)
|
||||
|
||||
if (!romfs_dirs || !romfs_files) {
|
||||
if (romfs_header.dirTableSize > sizeof(romfs_dirs) || romfs_header.fileTableSize > sizeof(romfs_files)) {
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, LibnxError_OutOfMemory));
|
||||
}
|
||||
|
||||
@@ -317,8 +317,6 @@ void NX_NORETURN loadNro(void) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
|
||||
free(romfs_dirs);
|
||||
free(romfs_files);
|
||||
fsStorageClose(&s);
|
||||
|
||||
strcpy(g_defaultNroPath, g_nextNroPath);
|
||||
@@ -337,40 +335,24 @@ void NX_NORETURN loadNro(void) {
|
||||
}
|
||||
|
||||
uint8_t *nrobuf = (uint8_t*) g_heapAddr;
|
||||
|
||||
NroStart* start = (NroStart*) (nrobuf + 0);
|
||||
header = (NroHeader*) (nrobuf + sizeof(NroStart));
|
||||
uint8_t* rest = (uint8_t*) (nrobuf + sizeof(NroStart) + sizeof(NroHeader));
|
||||
|
||||
FsFileSystem fs;
|
||||
FsFile f;
|
||||
u64 bytes_read = 0;
|
||||
s64 offset = 0;
|
||||
|
||||
if (R_FAILED(rc = fsOpenSdCardFileSystem(&fs))) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
|
||||
// don't fatal if we don't find the nro, exit to menu
|
||||
FsFile f;
|
||||
if (R_FAILED(rc = fsFsOpenFile(&fs, fixedNextNroPath, FsOpenMode_Read, &f))) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
|
||||
if (R_FAILED(rc = fsFileRead(&f, offset, start, sizeof(*start), FsReadOption_None, &bytes_read)) ||
|
||||
bytes_read != sizeof(*start)) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
offset += sizeof(*start);
|
||||
|
||||
if (R_FAILED(rc = fsFileRead(&f, offset, header, sizeof(*header), FsReadOption_None, &bytes_read)) ||
|
||||
bytes_read != sizeof(*header) || header->magic != NROHEADER_MAGIC) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
offset += sizeof(*header);
|
||||
|
||||
const size_t rest_size = header->size - (sizeof(NroStart) + sizeof(NroHeader));
|
||||
if (R_FAILED(rc = fsFileRead(&f, offset, rest, rest_size, FsReadOption_None, &bytes_read)) ||
|
||||
bytes_read != rest_size) {
|
||||
u64 bytes_read;
|
||||
if (R_FAILED(rc = fsFileRead(&f, 0, start, g_heapSize, FsReadOption_None, &bytes_read)) ||
|
||||
header->magic != NROHEADER_MAGIC ||
|
||||
bytes_read < sizeof(*start) + sizeof(*header) + header->size) {
|
||||
diagAbortWithResult(rc);
|
||||
}
|
||||
|
||||
@@ -513,13 +495,11 @@ bool __nx_fsdev_support_cwd = false;
|
||||
// u32 __nx_applet_exit_mode = 0;
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
static char g_innerheap[0x4000];
|
||||
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = &g_innerheap[0];
|
||||
fake_heap_end = &g_innerheap[sizeof(g_innerheap)];
|
||||
fake_heap_start = NULL;
|
||||
fake_heap_end = NULL;
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
@@ -557,7 +537,20 @@ void __appExit(void) {
|
||||
|
||||
}
|
||||
|
||||
// exit() effectively never gets called, so let's stub it out.
|
||||
void __wrap_exit(void) {
|
||||
// exit() effectively never gets called, so let's stub it out.
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 39));
|
||||
}
|
||||
|
||||
// stub alloc calls as they're not used (saves 4KiB).
|
||||
void* __libnx_alloc(size_t size) {
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 40));
|
||||
}
|
||||
|
||||
void* __libnx_aligned_alloc(size_t alignment, size_t size) {
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 41));
|
||||
}
|
||||
|
||||
void __libnx_free(void* p) {
|
||||
diagAbortWithResult(MAKERESULT(Module_HomebrewLoader, 43));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
set(sphaira_VERSION 0.11.2)
|
||||
# generic options.
|
||||
option(ENABLE_NVJPG "" OFF)
|
||||
option(ENABLE_NSZ "enables exporting to nsz" ON)
|
||||
|
||||
# lib options.
|
||||
option(ENABLE_LIBUSBHSFS "enables FAT/exFAT hdd mounting" ON)
|
||||
option(ENABLE_LIBUSBDVD "enables cd/dvd/iso/cue mounting" ON)
|
||||
option(ENABLE_FTPSRV "enables MTP server support" ON)
|
||||
option(ENABLE_LIBHAZE "enables MTP server support" ON)
|
||||
|
||||
# audio options.
|
||||
option(ENABLE_AUDIO_MP3 "" ON)
|
||||
option(ENABLE_AUDIO_OGG "" ON)
|
||||
option(ENABLE_AUDIO_WAV "" ON)
|
||||
option(ENABLE_AUDIO_FLAC "" ON)
|
||||
|
||||
# devoptab options.
|
||||
option(ENABLE_DEVOPTAB_HTTP "" ON)
|
||||
option(ENABLE_DEVOPTAB_NFS "" ON)
|
||||
option(ENABLE_DEVOPTAB_SMB2 "" ON)
|
||||
option(ENABLE_DEVOPTAB_FTP "" ON)
|
||||
option(ENABLE_DEVOPTAB_WEBDAV "" ON)
|
||||
# disable by default because we are CPU bound for upload/download.
|
||||
# max speed is 8MiB/s, which is fine for wifi, but awful for ethernet.
|
||||
# other clients get 36-40MiB/s.
|
||||
# it also adds 230k to binary size, and i don't think anyone will use it.
|
||||
option(ENABLE_DEVOPTAB_SFTP "" OFF)
|
||||
|
||||
set(sphaira_VERSION 1.0.0)
|
||||
|
||||
project(sphaira
|
||||
VERSION ${sphaira_VERSION}
|
||||
@@ -30,23 +58,32 @@ execute_process(
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
set(sphaira_VERSION_HASH "${sphaira_VERSION} [${GIT_COMMIT}]")
|
||||
if (DEFINED sphaira_VERSION_OVERRIDE)
|
||||
set(sphaira_DISPLAY_VERSION "${sphaira_VERSION_OVERRIDE} [${GIT_COMMIT}]")
|
||||
else()
|
||||
set(sphaira_DISPLAY_VERSION "${sphaira_VERSION} [${GIT_COMMIT}]")
|
||||
endif()
|
||||
|
||||
add_executable(sphaira
|
||||
source/ui/menus/appstore.cpp
|
||||
source/ui/menus/file_viewer.cpp
|
||||
source/ui/menus/image_viewer.cpp
|
||||
source/ui/menus/filebrowser.cpp
|
||||
source/ui/menus/file_picker.cpp
|
||||
source/ui/menus/homebrew.cpp
|
||||
source/ui/menus/irs_menu.cpp
|
||||
source/ui/menus/main_menu.cpp
|
||||
source/ui/menus/menu_base.cpp
|
||||
source/ui/menus/save_menu.cpp
|
||||
source/ui/menus/themezer.cpp
|
||||
source/ui/menus/ghdl.cpp
|
||||
source/ui/menus/usb_menu.cpp
|
||||
source/ui/menus/ftp_menu.cpp
|
||||
source/ui/menus/gc_menu.cpp
|
||||
source/ui/menus/game_menu.cpp
|
||||
source/ui/menus/game_meta_menu.cpp
|
||||
source/ui/menus/game_nca_menu.cpp
|
||||
source/ui/menus/grid_menu_base.cpp
|
||||
source/ui/menus/install_stream_menu_base.cpp
|
||||
|
||||
source/ui/error_box.cpp
|
||||
source/ui/notification.cpp
|
||||
@@ -59,6 +96,7 @@ add_executable(sphaira
|
||||
source/ui/widget.cpp
|
||||
source/ui/list.cpp
|
||||
source/ui/scrolling_text.cpp
|
||||
source/ui/music_player.cpp
|
||||
|
||||
source/app.cpp
|
||||
source/download.cpp
|
||||
@@ -74,36 +112,61 @@ add_executable(sphaira
|
||||
source/nxlink.cpp
|
||||
source/owo.cpp
|
||||
source/swkbd.cpp
|
||||
source/web.cpp
|
||||
source/hasher.cpp
|
||||
source/i18n.cpp
|
||||
source/ftpsrv_helper.cpp
|
||||
source/threaded_file_transfer.cpp
|
||||
source/title_info.cpp
|
||||
source/minizip_helper.cpp
|
||||
|
||||
source/utils/utils.cpp
|
||||
source/utils/audio.cpp
|
||||
source/utils/devoptab_common.cpp
|
||||
source/utils/devoptab_romfs.cpp
|
||||
source/utils/devoptab_save.cpp
|
||||
source/utils/devoptab_nro.cpp
|
||||
source/utils/devoptab_nca.cpp
|
||||
source/utils/devoptab_nsp.cpp
|
||||
source/utils/devoptab_xci.cpp
|
||||
source/utils/devoptab_zip.cpp
|
||||
source/utils/devoptab_bfsar.cpp
|
||||
source/utils/devoptab_vfs.cpp
|
||||
source/utils/devoptab_fatfs.cpp
|
||||
source/utils/devoptab_game.cpp
|
||||
source/utils/devoptab_mounts.cpp
|
||||
source/utils/devoptab.cpp
|
||||
|
||||
source/usb/base.cpp
|
||||
source/usb/usbds.cpp
|
||||
source/usb/usbhs.cpp
|
||||
source/usb/usb_uploader.cpp
|
||||
source/usb/usb_installer.cpp
|
||||
source/usb/usb_dumper.cpp
|
||||
|
||||
source/yati/yati.cpp
|
||||
source/yati/container/nsp.cpp
|
||||
source/yati/container/xci.cpp
|
||||
source/yati/source/file.cpp
|
||||
source/yati/source/usb.cpp
|
||||
source/yati/source/stream.cpp
|
||||
source/yati/source/stream_file.cpp
|
||||
|
||||
source/yati/nx/es.cpp
|
||||
source/yati/nx/keys.cpp
|
||||
source/yati/nx/nca.cpp
|
||||
source/yati/nx/ncz.cpp
|
||||
source/yati/nx/ncm.cpp
|
||||
source/yati/nx/ns.cpp
|
||||
|
||||
source/yati/nx/nxdumptool_rsa.c
|
||||
source/yati/nx/nxdumptool/save.c
|
||||
)
|
||||
|
||||
target_compile_definitions(sphaira PRIVATE
|
||||
-DAPP_VERSION="${sphaira_VERSION}"
|
||||
-DAPP_VERSION_HASH="${sphaira_VERSION_HASH}"
|
||||
-DAPP_DISPLAY_VERSION="${sphaira_DISPLAY_VERSION}"
|
||||
-DCURL_NO_OLDIES=1
|
||||
-DDEV_BUILD=$<BOOL:${DEV_BUILD}>
|
||||
-DZSTD_STATIC_LINKING_ONLY=1
|
||||
)
|
||||
|
||||
target_compile_options(sphaira PRIVATE
|
||||
@@ -125,6 +188,8 @@ target_compile_options(sphaira PRIVATE
|
||||
# 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
|
||||
# many false positives when LTO is not enabled.
|
||||
-Wno-suggest-final-types
|
||||
|
||||
# the below are taken from my gba emulator, they've served me well ;)
|
||||
-Wformat-overflow=2
|
||||
@@ -141,28 +206,15 @@ target_compile_options(sphaira PRIVATE
|
||||
-Wcast-qual
|
||||
-Wcast-align
|
||||
-Wimplicit-fallthrough=5
|
||||
-Wsuggest-final-types
|
||||
-Wuninitialized
|
||||
)
|
||||
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_QUIET FALSE)
|
||||
|
||||
FetchContent_Declare(ftpsrv
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git
|
||||
# GIT_TAG 1.2.2
|
||||
GIT_TAG 8c18431
|
||||
SOURCE_SUBDIR NONE
|
||||
)
|
||||
|
||||
FetchContent_Declare(libhaze
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libhaze.git
|
||||
GIT_TAG 8e16df2
|
||||
)
|
||||
|
||||
FetchContent_Declare(libpulsar
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/switch-libpulsar.git
|
||||
GIT_TAG de656e4
|
||||
GIT_TAG ac7bc97
|
||||
)
|
||||
|
||||
FetchContent_Declare(nanovg
|
||||
@@ -177,12 +229,12 @@ FetchContent_Declare(stb
|
||||
|
||||
FetchContent_Declare(yyjson
|
||||
GIT_REPOSITORY https://github.com/ibireme/yyjson.git
|
||||
GIT_TAG 0.10.0
|
||||
GIT_TAG 0.12.0
|
||||
)
|
||||
|
||||
FetchContent_Declare(minIni
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/minIni-nx.git
|
||||
GIT_TAG 11cac8b
|
||||
GIT_TAG 6e952b6
|
||||
)
|
||||
|
||||
FetchContent_Declare(zstd
|
||||
@@ -191,34 +243,301 @@ FetchContent_Declare(zstd
|
||||
SOURCE_SUBDIR build/cmake
|
||||
)
|
||||
|
||||
FetchContent_Declare(libusbhsfs
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libusbhsfs.git
|
||||
GIT_TAG db2bf2a
|
||||
FetchContent_Declare(libnxtc
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libnxtc.git
|
||||
GIT_TAG 88ce3d8
|
||||
)
|
||||
|
||||
set(USE_NEW_ZSTD ON)
|
||||
FetchContent_Declare(nvjpg
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/oss-nvjpg.git
|
||||
GIT_TAG 45680e7
|
||||
)
|
||||
|
||||
FetchContent_Declare(dr_libs
|
||||
GIT_REPOSITORY https://github.com/mackron/dr_libs.git
|
||||
GIT_TAG b962384
|
||||
SOURCE_SUBDIR NONE
|
||||
)
|
||||
|
||||
if (ENABLE_NVJPG)
|
||||
FetchContent_Declare(nvjpg
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/oss-nvjpg.git
|
||||
GIT_TAG 45680e7
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(nvjpg)
|
||||
|
||||
add_library(nvjpg
|
||||
${nvjpg_SOURCE_DIR}/lib/decoder.cpp
|
||||
${nvjpg_SOURCE_DIR}/lib/image.cpp
|
||||
${nvjpg_SOURCE_DIR}/lib/surface.cpp
|
||||
)
|
||||
|
||||
target_include_directories(nvjpg PUBLIC ${nvjpg_SOURCE_DIR}/include)
|
||||
set_target_properties(nvjpg PROPERTIES CXX_STANDARD 26)
|
||||
|
||||
target_link_libraries(nvjpg PRIVATE nvjpg)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_NVJPG)
|
||||
endif()
|
||||
|
||||
if (ENABLE_NSZ)
|
||||
target_sources(sphaira PRIVATE source/utils/nsz_dumper.cpp)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_NSZ)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSBHSFS)
|
||||
# enable this if you want ntfs and ext4 support, at the cost of a huge final binary size.
|
||||
set(USBHSFS_GPL OFF)
|
||||
set(USBHSFS_SXOS_DISABLE ON)
|
||||
|
||||
FetchContent_Declare(libusbhsfs
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libusbhsfs.git
|
||||
GIT_TAG 625269b
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libusbhsfs)
|
||||
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_LIBUSBHSFS)
|
||||
target_link_libraries(sphaira PRIVATE libusbhsfs)
|
||||
else()
|
||||
target_sources(sphaira PRIVATE source/ff16/ffunicode.c)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSBDVD)
|
||||
FetchContent_Declare(libusbdvd
|
||||
GIT_REPOSITORY https://github.com/proconsule/libusbdvd.git
|
||||
GIT_TAG 3cb0613
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libusbdvd)
|
||||
|
||||
add_library(libusbdvd
|
||||
${libusbdvd_SOURCE_DIR}/source/usbdvd.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/usbdvd_scsi.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/usbdvd_utils.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/usbdvd_datadisc.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/audiocdfs/audiocdfs.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/audiocdfs/cdaudio_devoptab.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/iso9660/usbdvd_iso9660.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/iso9660/iso9660_devoptab.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/udf/usbdvd_udf.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/fs/udf/udf_devoptab.cpp
|
||||
${libusbdvd_SOURCE_DIR}/source/os/switch/switch_usb.cpp
|
||||
|
||||
)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/os/switch)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/fs)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/fs/audiocdfs)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/fs/iso9660)
|
||||
target_include_directories(libusbdvd PRIVATE ${libusbdvd_SOURCE_DIR}/source/fs/udf)
|
||||
target_include_directories(libusbdvd PUBLIC ${libusbdvd_SOURCE_DIR}/include)
|
||||
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_LIBUSBDVD)
|
||||
target_link_libraries(sphaira PRIVATE libusbdvd)
|
||||
target_sources(sphaira PRIVATE source/usbdvd.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_FTPSRV)
|
||||
FetchContent_Declare(ftpsrv
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/ftpsrv.git
|
||||
GIT_TAG 7c82402
|
||||
SOURCE_SUBDIR NONE
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(ftpsrv)
|
||||
|
||||
set(FTPSRV_LIB_BUILD TRUE)
|
||||
set(FTPSRV_LIB_VFS_CUSTOM ${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs_nx.h)
|
||||
set(FTPSRV_LIB_PATH_SIZE 0x301)
|
||||
set(FTPSRV_LIB_SESSIONS 16)
|
||||
set(FTPSRV_LIB_BUF_SIZE 1024*64)
|
||||
|
||||
set(FTPSRV_LIB_CUSTOM_DEFINES
|
||||
USE_VFS_SAVE=$<BOOL:FALSE>
|
||||
USE_VFS_STORAGE=$<BOOL:TRUE>
|
||||
# disabled as it may conflict with the gamecard menu.
|
||||
USE_VFS_GC=$<BOOL:FALSE>
|
||||
USE_VFS_USBHSFS=$<BOOL:FALSE>
|
||||
VFS_NX_BUFFER_IO=$<BOOL:TRUE>
|
||||
# let sphaira handle init / closing of the hdd.
|
||||
USE_VFS_USBHSFS_INIT=$<BOOL:FALSE>
|
||||
# disable romfs mounting as otherwise we cannot write / modify sphaira.nro
|
||||
USE_VFS_ROMFS=$<BOOL:FALSE>
|
||||
FTP_SOCKET_HEADER="${ftpsrv_SOURCE_DIR}/src/platform/nx/socket_nx.h"
|
||||
)
|
||||
|
||||
add_subdirectory(${ftpsrv_SOURCE_DIR} binary_dir)
|
||||
|
||||
add_library(ftpsrv_helper
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs_nx.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_none.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_root.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_fs.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_storage.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/utils.c
|
||||
)
|
||||
|
||||
target_link_libraries(ftpsrv_helper PUBLIC ftpsrv)
|
||||
target_include_directories(ftpsrv_helper PUBLIC ${ftpsrv_SOURCE_DIR}/src/platform)
|
||||
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_FTPSRV)
|
||||
target_link_libraries(sphaira PRIVATE ftpsrv_helper)
|
||||
|
||||
target_sources(sphaira PRIVATE
|
||||
source/ftpsrv_helper.cpp
|
||||
source/ui/menus/ftp_menu.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBHAZE)
|
||||
FetchContent_Declare(libhaze
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libhaze.git
|
||||
GIT_TAG 81154c1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libhaze)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_LIBHAZE)
|
||||
target_link_libraries(sphaira PRIVATE libhaze)
|
||||
|
||||
target_sources(sphaira PRIVATE
|
||||
source/haze_helper.cpp
|
||||
source/ui/menus/mtp_menu.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_HTTP)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_HTTP)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_http.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_NFS)
|
||||
FetchContent_Declare(libnfs
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libnfs.git
|
||||
GIT_TAG 65f3e11
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libnfs)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_NFS)
|
||||
target_link_libraries(sphaira PRIVATE nfs)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_nfs.cpp)
|
||||
|
||||
# todo: fix this upstream as nfs should export these folders.
|
||||
target_include_directories(sphaira PRIVATE
|
||||
${libnfs_SOURCE_DIR}/include
|
||||
${libnfs_SOURCE_DIR}/include/nfsc
|
||||
${libnfs_SOURCE_DIR}/nfs
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_SMB2)
|
||||
FetchContent_Declare(libsmb2
|
||||
GIT_REPOSITORY https://github.com/ITotalJustice/libsmb2.git
|
||||
GIT_TAG 867beea
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libsmb2)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_SMB2)
|
||||
target_link_libraries(sphaira PRIVATE smb2)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_smb2.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_FTP)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_FTP)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_ftp.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_SFTP)
|
||||
# set to build from source, otherwise it will link against the older dkp libssh2.
|
||||
if (1)
|
||||
set(CRYPTO_BACKEND mbedTLS)
|
||||
set(ENABLE_ZLIB_COMPRESSION ON)
|
||||
set(ENABLE_DEBUG_LOGGING OFF)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
set(BUILD_TESTING OFF)
|
||||
set(LINT OFF)
|
||||
|
||||
FetchContent_Declare(libssh2
|
||||
GIT_REPOSITORY https://github.com/libssh2/libssh2.git
|
||||
# GIT_TAG a0dafb3 # latest commit, works fine, but i'll stick to main release.
|
||||
GIT_TAG libssh2-1.11.1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libssh2)
|
||||
target_link_libraries(sphaira PRIVATE libssh2::libssh2)
|
||||
else()
|
||||
include(FindPkgConfig)
|
||||
pkg_check_modules(LIBSSH2 libssh2 REQUIRED)
|
||||
target_include_directories(sphaira PRIVATE ${LIBSSH2_INCLUDE_DIRS})
|
||||
target_link_libraries(sphaira PRIVATE ${LIBSSH2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_SFTP)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_sftp.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DEVOPTAB_WEBDAV)
|
||||
set(PUGIXML_NO_EXCEPTIONS ON)
|
||||
set(PUGIXML_WCHAR_MODE OFF)
|
||||
|
||||
FetchContent_Declare(pugixml
|
||||
GIT_REPOSITORY https://github.com/zeux/pugixml.git
|
||||
GIT_TAG v1.15
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(pugixml)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_DEVOPTAB_WEBDAV)
|
||||
target_link_libraries(sphaira PRIVATE pugixml)
|
||||
target_sources(sphaira PRIVATE source/utils/devoptab_webdav.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_AUDIO_MP3)
|
||||
FetchContent_Declare(id3v2lib
|
||||
GIT_REPOSITORY https://github.com/larsbs/id3v2lib.git
|
||||
GIT_TAG 141ffb8
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(id3v2lib)
|
||||
target_link_libraries(sphaira PRIVATE id3v2lib)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_AUDIO_MP3)
|
||||
endif()
|
||||
|
||||
if (ENABLE_AUDIO_OGG)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_AUDIO_OGG)
|
||||
endif()
|
||||
|
||||
if (ENABLE_AUDIO_WAV)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_AUDIO_WAV)
|
||||
endif()
|
||||
|
||||
if (ENABLE_AUDIO_FLAC)
|
||||
target_compile_definitions(sphaira PRIVATE ENABLE_AUDIO_FLAC)
|
||||
endif()
|
||||
|
||||
# ztsd
|
||||
set(ZSTD_BUILD_STATIC ON)
|
||||
set(ZSTD_BUILD_SHARED OFF)
|
||||
set(ZSTD_BUILD_COMPRESSION OFF)
|
||||
set(ZSTD_BUILD_COMPRESSION ${ENABLE_NSZ})
|
||||
set(ZSTD_MULTITHREAD_SUPPORT ${ENABLE_NSZ})
|
||||
set(ZSTD_BUILD_DECOMPRESSION ON)
|
||||
set(ZSTD_BUILD_DICTBUILDER OFF)
|
||||
set(ZSTD_LEGACY_SUPPORT OFF)
|
||||
set(ZSTD_MULTITHREAD_SUPPORT OFF)
|
||||
set(ZSTD_BUILD_PROGRAMS OFF)
|
||||
set(ZSTD_BUILD_TESTS OFF)
|
||||
|
||||
# minini
|
||||
set(MININI_LIB_NAME minIni)
|
||||
set(MININI_USE_STDIO ON)
|
||||
set(MININI_USE_NX OFF)
|
||||
set(MININI_USE_FLOAT OFF)
|
||||
set(MININI_USE_FLOAT ON)
|
||||
|
||||
# nanovg
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(NANOVG_DEBUG ON)
|
||||
endif()
|
||||
set(NANOVG_NO_JPEG OFF)
|
||||
set(NANOVG_NO_PNG OFF)
|
||||
set(NANOVG_NO_BMP ON)
|
||||
set(NANOVG_NO_BMP OFF)
|
||||
set(NANOVG_NO_PSD ON)
|
||||
set(NANOVG_NO_TGA ON)
|
||||
set(NANOVG_NO_GIF ON)
|
||||
@@ -226,6 +545,8 @@ set(NANOVG_NO_HDR ON)
|
||||
set(NANOVG_NO_PIC ON)
|
||||
set(NANOVG_NO_PNM ON)
|
||||
|
||||
# yyjson
|
||||
set(YYJSON_INSTALL OFF)
|
||||
set(YYJSON_DISABLE_READER OFF)
|
||||
set(YYJSON_DISABLE_WRITER OFF)
|
||||
set(YYJSON_DISABLE_UTILS ON)
|
||||
@@ -234,62 +555,30 @@ set(YYJSON_DISABLE_NON_STANDARD ON)
|
||||
set(YYJSON_DISABLE_UTF8_VALIDATION ON)
|
||||
set(YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS OFF)
|
||||
|
||||
# enable this if you want ntfs and ext4 support, at the cost of a huge final binary size.
|
||||
set(USBHSFS_GPL OFF)
|
||||
set(USBHSFS_SXOS_DISABLE ON)
|
||||
|
||||
FetchContent_MakeAvailable(
|
||||
ftpsrv
|
||||
libhaze
|
||||
libpulsar
|
||||
nanovg
|
||||
stb
|
||||
minIni
|
||||
yyjson
|
||||
zstd
|
||||
libusbhsfs
|
||||
libnxtc
|
||||
dr_libs
|
||||
)
|
||||
|
||||
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)
|
||||
set(FTPSRV_LIB_PATH_SIZE 0x301)
|
||||
set(FTPSRV_LIB_SESSIONS 16)
|
||||
set(FTPSRV_LIB_BUF_SIZE 1024*64)
|
||||
|
||||
set(FTPSRV_LIB_CUSTOM_DEFINES
|
||||
USE_VFS_SAVE=$<BOOL:TRUE>
|
||||
USE_VFS_STORAGE=$<BOOL:TRUE>
|
||||
# disabled as it may conflict with the gamecard menu.
|
||||
USE_VFS_GC=$<BOOL:FALSE>
|
||||
USE_VFS_USBHSFS=$<BOOL:TRUE>
|
||||
VFS_NX_BUFFER_IO=$<BOOL:TRUE>
|
||||
# let sphaira handle init / closing of the hdd.
|
||||
USE_VFS_USBHSFS_INIT=$<BOOL:FALSE>
|
||||
# disable romfs mounting as otherwise we cannot write / modify sphaira.nro
|
||||
USE_VFS_ROMFS=$<BOOL:FALSE>
|
||||
)
|
||||
|
||||
add_subdirectory(${ftpsrv_SOURCE_DIR} binary_dir)
|
||||
|
||||
add_library(ftpsrv_helper
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs_nx.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_none.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_root.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_fs.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_save.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_storage.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_stdio.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/vfs/vfs_nx_hdd.c
|
||||
${ftpsrv_SOURCE_DIR}/src/platform/nx/utils.c
|
||||
)
|
||||
|
||||
target_link_libraries(ftpsrv_helper PUBLIC ftpsrv libusbhsfs)
|
||||
target_include_directories(ftpsrv_helper PUBLIC ${ftpsrv_SOURCE_DIR}/src/platform)
|
||||
|
||||
add_library(stb INTERFACE)
|
||||
target_include_directories(stb INTERFACE ${stb_SOURCE_DIR})
|
||||
|
||||
add_library(dr_libs INTERFACE)
|
||||
target_include_directories(dr_libs INTERFACE ${dr_libs_SOURCE_DIR})
|
||||
|
||||
add_library(libnxtc
|
||||
${libnxtc_SOURCE_DIR}/source/nxtc.c
|
||||
${libnxtc_SOURCE_DIR}/source/nxtc_log.c
|
||||
${libnxtc_SOURCE_DIR}/source/nxtc_utils.c
|
||||
)
|
||||
target_include_directories(libnxtc PUBLIC ${libnxtc_SOURCE_DIR}/include)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_library(minizip_lib minizip REQUIRED)
|
||||
find_path(minizip_inc minizip REQUIRED)
|
||||
@@ -298,10 +587,12 @@ find_package(CURL REQUIRED)
|
||||
find_path(mbedtls_inc mbedtls REQUIRED)
|
||||
find_library(mbedcrypto_lib mbedcrypto REQUIRED)
|
||||
|
||||
if (NOT USE_NEW_ZSTD)
|
||||
find_path(zstd_inc zstd.h REQUIRED)
|
||||
find_library(zstd_lib zstd REQUIRED)
|
||||
endif()
|
||||
add_library(fatfs
|
||||
source/ff16/diskio.c
|
||||
source/ff16/ff.c
|
||||
)
|
||||
|
||||
target_include_directories(fatfs PUBLIC source/ff16)
|
||||
|
||||
set_target_properties(sphaira PROPERTIES
|
||||
C_STANDARD 23
|
||||
@@ -311,14 +602,15 @@ set_target_properties(sphaira PROPERTIES
|
||||
)
|
||||
|
||||
target_link_libraries(sphaira PRIVATE
|
||||
ftpsrv_helper
|
||||
libhaze
|
||||
libpulsar
|
||||
minIni
|
||||
nanovg
|
||||
stb
|
||||
yyjson
|
||||
# libusbhsfs
|
||||
libnxtc
|
||||
fatfs
|
||||
dr_libs
|
||||
libzstd_static
|
||||
|
||||
${minizip_lib}
|
||||
ZLIB::ZLIB
|
||||
@@ -326,19 +618,11 @@ target_link_libraries(sphaira PRIVATE
|
||||
${mbedcrypto_lib}
|
||||
)
|
||||
|
||||
if (USE_NEW_ZSTD)
|
||||
message(STATUS "USING UPSTREAM ZSTD")
|
||||
target_link_libraries(sphaira PRIVATE libzstd_static)
|
||||
else()
|
||||
message(STATUS "USING LOCAL ZSTD")
|
||||
target_link_libraries(sphaira PRIVATE ${zstd_lib})
|
||||
target_include_directories(sphaira PRIVATE ${zstd_inc})
|
||||
endif()
|
||||
|
||||
target_include_directories(sphaira PRIVATE
|
||||
include
|
||||
${minizip_inc}
|
||||
${mbedtls_inc}
|
||||
include/yati/nx/nxdumptool
|
||||
)
|
||||
|
||||
# copy the romfs
|
||||
|
||||
@@ -2,32 +2,27 @@
|
||||
|
||||
#include "nanovg.h"
|
||||
#include "nanovg/dk_renderer.hpp"
|
||||
#include "pulsar.h"
|
||||
#include "ui/widget.hpp"
|
||||
#include "ui/notification.hpp"
|
||||
#include "owo.hpp"
|
||||
#include "option.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "log.hpp"
|
||||
#include "utils/audio.hpp"
|
||||
|
||||
#ifdef USE_NVJPG
|
||||
#include <nvjpg.hpp>
|
||||
#endif
|
||||
#include <switch.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <span>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace sphaira {
|
||||
|
||||
enum SoundEffect {
|
||||
SoundEffect_Music,
|
||||
SoundEffect_Focus,
|
||||
SoundEffect_Scroll,
|
||||
SoundEffect_Limit,
|
||||
SoundEffect_Startup,
|
||||
SoundEffect_Install,
|
||||
SoundEffect_Error,
|
||||
SoundEffect_MAX,
|
||||
};
|
||||
using SoundEffect = audio::SoundEffect;
|
||||
|
||||
enum class LaunchType {
|
||||
Normal,
|
||||
@@ -55,12 +50,19 @@ public:
|
||||
static void Exit();
|
||||
static void ExitRestart();
|
||||
static auto GetVg() -> NVGcontext*;
|
||||
static void Push(std::shared_ptr<ui::Widget>);
|
||||
|
||||
static void Push(std::unique_ptr<ui::Widget>&&);
|
||||
|
||||
template<ui::DerivedFromWidget T, typename... Args>
|
||||
static void Push(Args&&... args) {
|
||||
Push(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// pops all widgets above a menu
|
||||
static void PopToMenu();
|
||||
|
||||
// this is thread safe
|
||||
static void Notify(std::string text, ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
||||
static void Notify(const std::string& text, ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
||||
static void Notify(ui::NotifEntry entry);
|
||||
static void NotifyPop(ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
||||
static void NotifyClear(ui::NotifEntry::Side side = ui::NotifEntry::Side::RIGHT);
|
||||
@@ -92,12 +94,14 @@ public:
|
||||
static auto GetInstallSysmmcEnable() -> bool;
|
||||
static auto GetInstallEmummcEnable() -> bool;
|
||||
static auto GetInstallSdEnable() -> bool;
|
||||
static auto GetInstallPrompt() -> bool;
|
||||
static auto GetThemeMusicEnable() -> bool;
|
||||
static auto Get12HourTimeEnable() -> bool;
|
||||
static auto GetLanguage() -> long;
|
||||
static auto GetTextScrollSpeed() -> long;
|
||||
|
||||
static auto GetNszCompressLevel() -> u8;
|
||||
static auto GetNszThreadCount() -> u8;
|
||||
static auto GetNszBlockExponent() -> u8;
|
||||
|
||||
static void SetMtpEnable(bool enable);
|
||||
static void SetFtpEnable(bool enable);
|
||||
static void SetNxlinkEnable(bool enable);
|
||||
@@ -122,10 +126,18 @@ public:
|
||||
static void DisplayThemeOptions(bool left_side = true);
|
||||
// todo:
|
||||
static void DisplayNetworkOptions(bool left_side = true);
|
||||
static void DisplayMiscOptions(bool left_side = true);
|
||||
static void DisplayMenuOptions(bool left_side = true);
|
||||
static void DisplayAdvancedOptions(bool left_side = true);
|
||||
static void DisplayInstallOptions(bool left_side = true);
|
||||
static void DisplayDumpOptions(bool left_side = true);
|
||||
static void DisplayFtpOptions(bool left_side = true);
|
||||
static void DisplayMtpOptions(bool left_side = true);
|
||||
static void DisplayHddOptions(bool left_side = true);
|
||||
|
||||
// helper for sidebar options to toggle install on/off
|
||||
static void ShowEnableInstallPromptOption(option::OptionBool& option, bool& enable);
|
||||
// displays an option box to enable installing, shows warning.
|
||||
static void ShowEnableInstallPrompt();
|
||||
|
||||
void Draw();
|
||||
void Update();
|
||||
@@ -138,8 +150,15 @@ public:
|
||||
|
||||
void LoadTheme(const ThemeMeta& meta);
|
||||
void CloseTheme();
|
||||
void CloseThemeBackgroundMusic();
|
||||
void ScanThemes(const std::string& path);
|
||||
void ScanThemeEntries();
|
||||
void LoadAndPlayThemeMusic();
|
||||
static Result SetDefaultBackgroundMusic(fs::Fs* fs, const fs::FsPath& path);
|
||||
static void SetBackgroundMusicPause(bool pause);
|
||||
|
||||
static Result GetSdSize(s64* free, s64* total);
|
||||
static Result GetEmmcSize(s64* free, s64* total);
|
||||
|
||||
// helper that converts 1.2.3 to a u32 used for comparisons.
|
||||
static auto GetVersionFromString(const char* str) -> u32;
|
||||
@@ -189,11 +208,59 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void SetBoostMode(bool enable, bool force = false) {
|
||||
static Mutex mutex{};
|
||||
static int ref_count{};
|
||||
|
||||
mutexLock(&mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&mutex));
|
||||
|
||||
if (enable) {
|
||||
ref_count++;
|
||||
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
|
||||
} else {
|
||||
if (ref_count) {
|
||||
ref_count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref_count || force) {
|
||||
ref_count = 0;
|
||||
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
|
||||
}
|
||||
}
|
||||
|
||||
static auto GetAccountList() -> std::vector<AccountProfileBase> {
|
||||
std::vector<AccountProfileBase> out;
|
||||
|
||||
AccountUid uids[ACC_USER_LIST_SIZE];
|
||||
s32 account_count;
|
||||
if (R_SUCCEEDED(accountListAllUsers(uids, std::size(uids), &account_count))) {
|
||||
for (s32 i = 0; i < account_count; i++) {
|
||||
AccountProfile profile;
|
||||
if (R_SUCCEEDED(accountGetProfile(&profile, uids[i]))) {
|
||||
ON_SCOPE_EXIT(accountProfileClose(&profile));
|
||||
|
||||
AccountProfileBase base;
|
||||
if (R_SUCCEEDED(accountProfileGet(&profile, nullptr, &base))) {
|
||||
// sometimes the uid for the acc can differ to the base.
|
||||
base.uid = uids[i];
|
||||
log_write("[ACC] found uid: 0x%016lX%016lX\n", uids[i].uid[0], uids[i].uid[1]);
|
||||
log_write("[ACC] base uid: 0x%016lX%016lX\n", base.uid.uid[0], base.uid.uid[1]);
|
||||
out.emplace_back(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// private:
|
||||
static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini";
|
||||
static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini";
|
||||
static constexpr inline auto INI_SECTION = "config";
|
||||
static constexpr inline auto DEFAULT_THEME_PATH = "romfs:/themes/abyss_theme.ini";
|
||||
static constexpr inline auto DEFAULT_THEME_PATH = "romfs:/themes/default_theme.ini";
|
||||
|
||||
fs::FsPath m_app_path;
|
||||
u64 m_start_timestamp{};
|
||||
@@ -205,11 +272,12 @@ public:
|
||||
PadState m_pad{};
|
||||
TouchInfo m_touch_info{};
|
||||
Controller m_controller{};
|
||||
KeyboardState m_keyboard{};
|
||||
std::vector<ThemeMeta> m_theme_meta_entries;
|
||||
|
||||
Vec2 m_scale{1, 1};
|
||||
|
||||
std::vector<std::shared_ptr<ui::Widget>> m_widgets;
|
||||
std::vector<std::unique_ptr<ui::Widget>> m_widgets;
|
||||
u32 m_pop_count{};
|
||||
ui::NotifMananger m_notif_manager{};
|
||||
|
||||
@@ -231,10 +299,12 @@ public:
|
||||
|
||||
option::OptionBool m_log_enabled{INI_SECTION, "log_enabled", false};
|
||||
option::OptionBool m_replace_hbmenu{INI_SECTION, "replace_hbmenu", false};
|
||||
option::OptionString m_default_music{INI_SECTION, "default_music", "/config/sphaira/themes/default_music.bfstm"};
|
||||
option::OptionString m_theme_path{INI_SECTION, "theme", DEFAULT_THEME_PATH};
|
||||
option::OptionBool m_theme_music{INI_SECTION, "theme_music", true};
|
||||
option::OptionBool m_12hour_time{INI_SECTION, "12hour_time", false};
|
||||
option::OptionBool m_show_ip_addr{INI_SECTION, "show_ip_addr", true};
|
||||
option::OptionLong m_language{INI_SECTION, "language", 0}; // auto
|
||||
option::OptionString m_center_menu{INI_SECTION, "center_side_menu", "Homebrew"};
|
||||
option::OptionString m_left_menu{INI_SECTION, "left_side_menu", "FileBrowser"};
|
||||
option::OptionString m_right_menu{INI_SECTION, "right_side_menu", "Appstore"};
|
||||
option::OptionBool m_progress_boost_mode{INI_SECTION, "progress_boost_mode", true};
|
||||
@@ -243,7 +313,6 @@ public:
|
||||
option::OptionBool m_install_sysmmc{INI_SECTION, "install_sysmmc", false};
|
||||
option::OptionBool m_install_emummc{INI_SECTION, "install_emummc", false};
|
||||
option::OptionBool m_install_sd{INI_SECTION, "install_sd", true};
|
||||
option::OptionLong m_install_prompt{INI_SECTION, "install_prompt", true};
|
||||
option::OptionBool m_allow_downgrade{INI_SECTION, "allow_downgrade", false};
|
||||
option::OptionBool m_skip_if_already_installed{INI_SECTION, "skip_if_already_installed", true};
|
||||
option::OptionBool m_ticket_only{INI_SECTION, "ticket_only", false};
|
||||
@@ -252,25 +321,75 @@ public:
|
||||
option::OptionBool m_skip_addon{INI_SECTION, "skip_addon", false};
|
||||
option::OptionBool m_skip_data_patch{INI_SECTION, "skip_data_patch", false};
|
||||
option::OptionBool m_skip_ticket{INI_SECTION, "skip_ticket", false};
|
||||
option::OptionBool m_skip_nca_hash_verify{INI_SECTION, "skip_nca_hash_verify", false};
|
||||
option::OptionBool m_skip_rsa_header_fixed_key_verify{INI_SECTION, "skip_rsa_header_fixed_key_verify", false};
|
||||
option::OptionBool m_skip_rsa_npdm_fixed_key_verify{INI_SECTION, "skip_rsa_npdm_fixed_key_verify", false};
|
||||
option::OptionBool m_skip_nca_hash_verify{INI_SECTION, "skip_nca_hash_verify", true};
|
||||
option::OptionBool m_skip_rsa_header_fixed_key_verify{INI_SECTION, "skip_rsa_header_fixed_key_verify", true};
|
||||
option::OptionBool m_skip_rsa_npdm_fixed_key_verify{INI_SECTION, "skip_rsa_npdm_fixed_key_verify", true};
|
||||
option::OptionBool m_ignore_distribution_bit{INI_SECTION, "ignore_distribution_bit", false};
|
||||
option::OptionBool m_convert_to_common_ticket{INI_SECTION, "convert_to_common_ticket", true};
|
||||
option::OptionBool m_convert_to_standard_crypto{INI_SECTION, "convert_to_standard_crypto", false};
|
||||
option::OptionBool m_lower_master_key{INI_SECTION, "lower_master_key", false};
|
||||
option::OptionBool m_lower_system_version{INI_SECTION, "lower_system_version", false};
|
||||
option::OptionBool m_lower_system_version{INI_SECTION, "lower_system_version", true};
|
||||
|
||||
// dump options
|
||||
option::OptionBool m_dump_app_folder{"dump", "app_folder", true};
|
||||
option::OptionBool m_dump_append_folder_with_xci{"dump", "append_folder_with_xci", true};
|
||||
option::OptionBool m_dump_trim_xci{"dump", "trim_xci", false};
|
||||
option::OptionBool m_dump_label_trim_xci{"dump", "label_trim_xci", false};
|
||||
option::OptionBool m_dump_usb_transfer_stream{"dump", "usb_transfer_stream", true};
|
||||
option::OptionBool m_dump_convert_to_common_ticket{"dump", "convert_to_common_ticket", true};
|
||||
option::OptionLong m_nsz_compress_level{"dump", "nsz_compress_level", 3};
|
||||
option::OptionLong m_nsz_compress_threads{"dump", "nsz_compress_threads", 3};
|
||||
option::OptionBool m_nsz_compress_ldm{"dump", "nsz_compress_ldm", true};
|
||||
option::OptionBool m_nsz_compress_block{"dump", "nsz_compress_block", false};
|
||||
option::OptionLong m_nsz_compress_block_exponent{"dump", "nsz_compress_block_exponent", 6};
|
||||
|
||||
// todo: move this into it's own menu
|
||||
option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal
|
||||
|
||||
PLSR_PlayerSoundId m_sound_ids[SoundEffect_MAX]{};
|
||||
// ftp options.
|
||||
option::OptionLong m_ftp_port{"ftp", "port", 5000};
|
||||
option::OptionBool m_ftp_anon{"ftp", "anon", true};
|
||||
option::OptionString m_ftp_user{"ftp", "user", ""};
|
||||
option::OptionString m_ftp_pass{"ftp", "pass", ""};
|
||||
option::OptionBool m_ftp_show_album{"ftp", "show_album", true};
|
||||
option::OptionBool m_ftp_show_ams_contents{"ftp", "show_ams_contents", false};
|
||||
option::OptionBool m_ftp_show_bis_storage{"ftp", "show_bis_storage", false};
|
||||
option::OptionBool m_ftp_show_bis_fs{"ftp", "show_bis_fs", false};
|
||||
option::OptionBool m_ftp_show_content_system{"ftp", "show_content_system", false};
|
||||
option::OptionBool m_ftp_show_content_user{"ftp", "show_content_user", false};
|
||||
option::OptionBool m_ftp_show_content_sd{"ftp", "show_content_sd", false};
|
||||
// option::OptionBool m_ftp_show_content_sd0{"ftp", "show_content_sd0", false};
|
||||
// option::OptionBool m_ftp_show_custom_system{"ftp", "show_custom_system", false};
|
||||
// option::OptionBool m_ftp_show_custom_sd{"ftp", "show_custom_sd", false};
|
||||
option::OptionBool m_ftp_show_games{"ftp", "show_games", true};
|
||||
option::OptionBool m_ftp_show_install{"ftp", "show_install", true};
|
||||
option::OptionBool m_ftp_show_mounts{"ftp", "show_mounts", false};
|
||||
option::OptionBool m_ftp_show_switch{"ftp", "show_switch", false};
|
||||
|
||||
// mtp options.
|
||||
option::OptionLong m_mtp_vid{"mtp", "vid", 0x057e}; // nintendo (hidden from ui)
|
||||
option::OptionLong m_mtp_pid{"mtp", "pid", 0x201d}; // switch (hidden from ui)
|
||||
option::OptionBool m_mtp_allocate_file{"mtp", "allocate_file", true};
|
||||
option::OptionBool m_mtp_show_album{"mtp", "show_album", true};
|
||||
option::OptionBool m_mtp_show_content_sd{"mtp", "show_content_sd", false};
|
||||
option::OptionBool m_mtp_show_content_system{"mtp", "show_content_system", false};
|
||||
option::OptionBool m_mtp_show_content_user{"mtp", "show_content_user", false};
|
||||
option::OptionBool m_mtp_show_games{"mtp", "show_games", true};
|
||||
option::OptionBool m_mtp_show_install{"mtp", "show_install", true};
|
||||
option::OptionBool m_mtp_show_mounts{"mtp", "show_mounts", false};
|
||||
option::OptionBool m_mtp_show_speedtest{"mtp", "show_speedtest", false};
|
||||
|
||||
std::shared_ptr<fs::FsNativeSd> m_fs{};
|
||||
audio::SongID m_background_music{};
|
||||
|
||||
#ifdef USE_NVJPG
|
||||
nj::Decoder m_decoder;
|
||||
#endif
|
||||
|
||||
double m_delta_time{};
|
||||
|
||||
static constexpr const char* INSTALL_DEPENDS_STR =
|
||||
"Installing is disabled.\n\n"
|
||||
"Enable in the options by selecting Menu (Y) -> Advanced -> Install options -> Enable.";
|
||||
|
||||
private: // from nanovg decko3d example by adubbz
|
||||
static constexpr unsigned NumFramebuffers = 2;
|
||||
|
||||
@@ -174,6 +174,7 @@ enum {
|
||||
Module_Npln = 321,
|
||||
Module_Tspm = 499,
|
||||
Module_Devmenu = 500,
|
||||
Module_Sphaira = 505,
|
||||
};
|
||||
|
||||
enum SvcError {
|
||||
@@ -494,7 +495,337 @@ enum NcmError {
|
||||
NcmError_WriteToReadOnlyContentStorage = 0x17C05,
|
||||
};
|
||||
|
||||
#define R_SUCCEED() return 0
|
||||
enum class SphairaResult : Result {
|
||||
TransferCancelled,
|
||||
StreamBadSeek,
|
||||
|
||||
FsTooManyEntries,
|
||||
FsNewPathTooLarge,
|
||||
FsInvalidType,
|
||||
FsEmpty,
|
||||
FsAlreadyRoot,
|
||||
FsNoCurrentPath,
|
||||
FsBrokenCurrentPath,
|
||||
FsIndexOutOfBounds,
|
||||
FsFsNotActive,
|
||||
FsNewPathEmpty,
|
||||
FsLoadingCancelled,
|
||||
FsBrokenRoot,
|
||||
|
||||
FsUnknownStdioError,
|
||||
FsStdioFailedToSeek,
|
||||
FsStdioFailedToRead,
|
||||
FsStdioFailedToWrite,
|
||||
FsStdioFailedToOpenFile,
|
||||
FsStdioFailedToCreate,
|
||||
FsStdioFailedToTruncate,
|
||||
FsStdioFailedToFlush,
|
||||
FsStdioFailedToCreateDirectory,
|
||||
FsStdioFailedToDeleteFile,
|
||||
FsStdioFailedToDeleteDirectory,
|
||||
FsStdioFailedToOpenDirectory,
|
||||
FsStdioFailedToRename,
|
||||
FsStdioFailedToStat,
|
||||
|
||||
FsReadOnly,
|
||||
FsNotActive,
|
||||
FsFailedStdioStat,
|
||||
FsFailedStdioOpendir,
|
||||
|
||||
NroBadMagic,
|
||||
NroBadSize,
|
||||
|
||||
AppFailedMusicDownload,
|
||||
CurlFailedEasyInit,
|
||||
DumpFailedNetworkUpload,
|
||||
|
||||
UnzOpen2_64,
|
||||
UnzGetGlobalInfo64,
|
||||
UnzLocateFile,
|
||||
UnzGoToFirstFile,
|
||||
UnzGoToNextFile,
|
||||
UnzOpenCurrentFile,
|
||||
UnzGetCurrentFileInfo64,
|
||||
UnzReadCurrentFile,
|
||||
|
||||
ZipOpen2_64,
|
||||
ZipOpenNewFileInZip,
|
||||
ZipWriteInFileInZip,
|
||||
|
||||
MmzBadLocalHeaderSig,
|
||||
MmzBadLocalHeaderRead,
|
||||
|
||||
FileBrowserFailedUpload,
|
||||
FileBrowserDirNotDaybreak,
|
||||
|
||||
AppstoreFailedZipDownload,
|
||||
AppstoreFailedMd5,
|
||||
AppstoreFailedParseManifest,
|
||||
|
||||
GameBadReadForDump,
|
||||
GameEmptyMetaEntries,
|
||||
GameMultipleKeysFound,
|
||||
GameNoNspEntriesBuilt,
|
||||
|
||||
KeyMissingNcaKeyArea,
|
||||
KeyMissingTitleKek,
|
||||
KeyMissingMasterKey,
|
||||
KeyFailedDecyptETicketDeviceKey,
|
||||
|
||||
NcaFailedNcaHeaderHashVerify,
|
||||
NcaBadSigKeyGen,
|
||||
|
||||
GcBadReadForDump,
|
||||
GcEmptyGamecard,
|
||||
GcBadXciMagic,
|
||||
GcBadXciRomSize,
|
||||
GcFailedToGetSecurityInfo,
|
||||
|
||||
GhdlEmptyAsset,
|
||||
GhdlFailedToDownloadAsset,
|
||||
GhdlFailedToDownloadAssetJson,
|
||||
|
||||
ThemezerFailedToDownloadThemeMeta,
|
||||
ThemezerFailedToDownloadTheme,
|
||||
|
||||
MainFailedToDownloadUpdate,
|
||||
|
||||
UsbDsBadDeviceSpeed,
|
||||
|
||||
NcaBadMagic,
|
||||
NspBadMagic,
|
||||
XciBadMagic,
|
||||
XciSecurePartitionNotFound,
|
||||
|
||||
EsBadTitleKeyType,
|
||||
EsPersonalisedTicketDeviceIdMissmatch,
|
||||
EsFailedDecryptPersonalisedTicket,
|
||||
EsBadDecryptedPersonalisedTicketSize,
|
||||
EsBadTicketSize,
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
EsInvalidTicketBadRightsId,
|
||||
EsInvalidTicketFromatVersion,
|
||||
EsInvalidTicketKeyType,
|
||||
EsInvalidTicketKeyRevision,
|
||||
|
||||
OwoBadArgs,
|
||||
|
||||
UsbCancelled,
|
||||
UsbBadMagic,
|
||||
UsbBadVersion,
|
||||
UsbBadCount,
|
||||
UsbBadBufferAlign,
|
||||
UsbBadTransferSize,
|
||||
UsbEmptyTransferSize,
|
||||
UsbOverflowTransferSize,
|
||||
UsbBadTotalSize,
|
||||
|
||||
UsbUploadBadMagic,
|
||||
UsbUploadExit,
|
||||
UsbUploadBadCount,
|
||||
UsbUploadBadTransferSize,
|
||||
UsbUploadBadTotalSize,
|
||||
UsbUploadBadCommand,
|
||||
|
||||
// unkown container for the source provided.
|
||||
YatiContainerNotFound,
|
||||
// nca required by the cnmt but not found in collection.
|
||||
YatiNcaNotFound,
|
||||
YatiInvalidNcaReadSize,
|
||||
YatiInvalidNcaSigKeyGen,
|
||||
YatiInvalidNcaMagic,
|
||||
YatiInvalidNcaSignature0,
|
||||
YatiInvalidNcaSignature1,
|
||||
// invalid sha256 over the entire nca.
|
||||
YatiInvalidNcaSha256,
|
||||
// section could not be found.
|
||||
YatiNczSectionNotFound,
|
||||
// section count == 0.
|
||||
YatiInvalidNczSectionCount,
|
||||
// block could not be found.
|
||||
YatiNczBlockNotFound,
|
||||
// block version != 2.
|
||||
YatiInvalidNczBlockVersion,
|
||||
// block type != 1.
|
||||
YatiInvalidNczBlockType,
|
||||
// block count == 0.
|
||||
YatiInvalidNczBlockTotal,
|
||||
// block size exponent < 14 || > 32.
|
||||
YatiInvalidNczBlockSizeExponent,
|
||||
// zstd error while decompressing ncz.
|
||||
YatiInvalidNczZstdError,
|
||||
// nca has rights_id but matching ticket wasn't found.
|
||||
YatiTicketNotFound,
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
YatiInvalidTicketBadRightsId,
|
||||
// cert not found for the ticket.
|
||||
YatiCertNotFound,
|
||||
// unable to fetch header from ncm database.
|
||||
YatiNcmDbCorruptHeader,
|
||||
// unable to total infos from ncm database.
|
||||
YatiNcmDbCorruptInfos,
|
||||
|
||||
NszFailedCreateCctx,
|
||||
NszFailedSetCompressionLevel,
|
||||
NszFailedSetThreadCount,
|
||||
NszFailedSetLongDistanceMode,
|
||||
NszFailedResetCctx,
|
||||
NszFailedCompress2,
|
||||
NszFailedCompressStream2,
|
||||
NszTooManyBlocks,
|
||||
// set when nca finished but not all blocks were handled.
|
||||
NszMissingBlocks,
|
||||
};
|
||||
|
||||
#define MAKE_SPHAIRA_RESULT_ENUM(x) Result_##x = MAKERESULT(Module_Sphaira, (Result)SphairaResult::x)
|
||||
|
||||
enum : Result {
|
||||
MAKE_SPHAIRA_RESULT_ENUM(TransferCancelled),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(StreamBadSeek),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsTooManyEntries),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsNewPathTooLarge),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsInvalidType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsEmpty),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsAlreadyRoot),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsNoCurrentPath),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsBrokenCurrentPath),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsIndexOutOfBounds),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsFsNotActive),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsNewPathEmpty),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsLoadingCancelled),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsBrokenRoot),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsUnknownStdioError),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToSeek),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToRead),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToWrite),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToOpenFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToCreate),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToTruncate),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToFlush),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToCreateDirectory),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToDeleteFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToDeleteDirectory),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToOpenDirectory),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToRename),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsStdioFailedToStat),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsReadOnly),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsNotActive),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsFailedStdioStat),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FsFailedStdioOpendir),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NroBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NroBadSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(AppFailedMusicDownload),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(CurlFailedEasyInit),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(DumpFailedNetworkUpload),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzOpen2_64),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzGetGlobalInfo64),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzLocateFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzGoToFirstFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzGoToNextFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzOpenCurrentFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzGetCurrentFileInfo64),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UnzReadCurrentFile),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(ZipOpen2_64),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(ZipOpenNewFileInZip),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(ZipWriteInFileInZip),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(MmzBadLocalHeaderSig),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(MmzBadLocalHeaderRead),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FileBrowserFailedUpload),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(FileBrowserDirNotDaybreak),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(AppstoreFailedZipDownload),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(AppstoreFailedMd5),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(AppstoreFailedParseManifest),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GameBadReadForDump),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GameEmptyMetaEntries),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GameMultipleKeysFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GameNoNspEntriesBuilt),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(KeyMissingNcaKeyArea),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(KeyMissingTitleKek),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(KeyMissingMasterKey),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(KeyFailedDecyptETicketDeviceKey),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NcaFailedNcaHeaderHashVerify),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NcaBadSigKeyGen),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GcBadReadForDump),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GcEmptyGamecard),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GcBadXciMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GcBadXciRomSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GcFailedToGetSecurityInfo),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GhdlEmptyAsset),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GhdlFailedToDownloadAsset),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(GhdlFailedToDownloadAssetJson),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(ThemezerFailedToDownloadThemeMeta),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(ThemezerFailedToDownloadTheme),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(MainFailedToDownloadUpdate),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbDsBadDeviceSpeed),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NspBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(XciBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NcaBadMagic),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(XciSecurePartitionNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsBadTitleKeyType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsPersonalisedTicketDeviceIdMissmatch),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsFailedDecryptPersonalisedTicket),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsBadDecryptedPersonalisedTicketSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsBadTicketSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketBadRightsId),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketFromatVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketKeyType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketKeyRevision),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(OwoBadArgs),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbCancelled),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadCount),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadBufferAlign),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadTransferSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbEmptyTransferSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbOverflowTransferSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadExit),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCount),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadTransferSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadTotalSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCommand),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiContainerNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNcaNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaReadSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaSigKeyGen),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaSignature0),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaSignature1),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNcaSha256),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNczSectionNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczSectionCount),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNczBlockNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczBlockVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczBlockType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczBlockTotal),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczBlockSizeExponent),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczZstdError),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiTicketNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidTicketBadRightsId),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiCertNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNcmDbCorruptHeader),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNcmDbCorruptInfos),
|
||||
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedCreateCctx),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedSetCompressionLevel),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedSetThreadCount),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedSetLongDistanceMode),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedResetCctx),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedCompress2),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszFailedCompressStream2),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszTooManyBlocks),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(NszMissingBlocks),
|
||||
};
|
||||
|
||||
#undef MAKE_SPHAIRA_RESULT_ENUM
|
||||
|
||||
#define R_SUCCEED() return (Result)0
|
||||
|
||||
#define R_THROW(_rc) return _rc
|
||||
|
||||
@@ -520,19 +851,83 @@ enum NcmError {
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __COUNTER__)
|
||||
|
||||
#define ON_SCOPE_EXIT(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { _f; }};
|
||||
#define ON_SCOPE_FAIL(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_FAILED(rc)) { _f; } }};
|
||||
#define ON_SCOPE_SUCCESS(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_SUCCEEDED(rc)) { _f; } }};
|
||||
template<typename Function>
|
||||
struct ScopeGuard {
|
||||
ScopeGuard(Function&& function) : m_function(std::forward<Function>(function)) {
|
||||
|
||||
// threading helpers.
|
||||
#define PRIO_PREEMPTIVE 0x3B
|
||||
}
|
||||
~ScopeGuard() {
|
||||
m_function();
|
||||
}
|
||||
|
||||
// threading affinity, use with svcSetThreadCoreMask().
|
||||
#define THREAD_AFFINITY_CORE0 BIT(0)
|
||||
#define THREAD_AFFINITY_CORE1 BIT(1)
|
||||
#define THREAD_AFFINITY_CORE2 BIT(2)
|
||||
#define THREAD_AFFINITY_DEFAULT(core) (BIT(core)|THREAD_AFFINITY_CORE1|THREAD_AFFINITY_CORE2)
|
||||
#define THREAD_AFFINITY_ALL (THREAD_AFFINITY_CORE0|THREAD_AFFINITY_CORE1|THREAD_AFFINITY_CORE2)
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
void operator=(const ScopeGuard&) = delete;
|
||||
|
||||
// mutex helpers.
|
||||
#define SCOPED_MUTEX(mutex) mutexLock(mutex); ON_SCOPE_EXIT(mutexUnlock(mutex))
|
||||
private:
|
||||
const Function m_function;
|
||||
};
|
||||
|
||||
struct ScopedMutex {
|
||||
ScopedMutex(Mutex* mutex) : m_mutex{mutex} {
|
||||
mutexLock(m_mutex);
|
||||
}
|
||||
~ScopedMutex() {
|
||||
mutexUnlock(m_mutex);
|
||||
}
|
||||
|
||||
ScopedMutex(const ScopedMutex&) = delete;
|
||||
void operator=(const ScopedMutex&) = delete;
|
||||
|
||||
private:
|
||||
Mutex* const m_mutex;
|
||||
};
|
||||
|
||||
struct ScopedRMutex {
|
||||
ScopedRMutex(RMutex* _mutex) : mutex{_mutex} {
|
||||
rmutexLock(mutex);
|
||||
}
|
||||
|
||||
~ScopedRMutex() {
|
||||
rmutexUnlock(mutex);
|
||||
}
|
||||
|
||||
ScopedRMutex(const ScopedRMutex&) = delete;
|
||||
void operator=(const ScopedRMutex&) = delete;
|
||||
|
||||
private:
|
||||
RMutex* const mutex;
|
||||
};
|
||||
|
||||
struct ScopedRwLock {
|
||||
ScopedRwLock(RwLock* _lock, bool _write) : lock{_lock}, write{_write} {
|
||||
if (write) {
|
||||
rwlockWriteLock(lock);
|
||||
} else {
|
||||
rwlockReadLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedRwLock() {
|
||||
if (write) {
|
||||
rwlockWriteUnlock(lock);
|
||||
} else {
|
||||
rwlockReadUnlock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedRwLock(const ScopedRwLock&) = delete;
|
||||
void operator=(const ScopedRwLock&) = delete;
|
||||
|
||||
private:
|
||||
RwLock* const lock;
|
||||
bool const write;
|
||||
};
|
||||
|
||||
// #define ON_SCOPE_EXIT(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { _f; }};
|
||||
#define ON_SCOPE_EXIT(_f) ScopeGuard ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { _f; }};
|
||||
#define SCOPED_MUTEX(_m) ScopedMutex ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m}
|
||||
#define SCOPED_RMUTEX(_m) ScopedRMutex ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m}
|
||||
#define SCOPED_RWLOCK(_m, _write) ScopedRwLock ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){_m, _write}
|
||||
|
||||
// #define ON_SCOPE_FAIL(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_FAILED(rc)) { _f; } }};
|
||||
// #define ON_SCOPE_SUCCESS(_f) std::experimental::scope_exit ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_){[&] { if (R_SUCCEEDED(rc)) { _f; } }};
|
||||
|
||||
@@ -52,7 +52,7 @@ struct Fields {
|
||||
|
||||
struct Header {
|
||||
Header() = default;
|
||||
Header(std::initializer_list<std::pair<const std::string, std::string>> p) : m_map{p} {}
|
||||
Header(std::initializer_list<std::pair<const std::string, std::string>>&& p) : m_map{std::forward<decltype(p)>(p)} {}
|
||||
std::unordered_map<std::string, std::string> m_map;
|
||||
|
||||
auto Find(const std::string& key) const {
|
||||
@@ -91,7 +91,7 @@ struct UserPass {
|
||||
struct UploadInfo {
|
||||
UploadInfo() = default;
|
||||
UploadInfo(const std::string& name) : m_name{name} {}
|
||||
UploadInfo(const std::string& name, s64 size, OnUploadCallback cb) : m_name{name}, m_size{size}, m_callback{cb} {}
|
||||
UploadInfo(const std::string& name, s64 size, const OnUploadCallback& cb) : m_name{name}, m_size{size}, m_callback{cb} {}
|
||||
UploadInfo(const std::string& name, const std::vector<u8>& data) : m_name{name}, m_data{data} {}
|
||||
std::string m_name{};
|
||||
std::vector<u8> m_data{};
|
||||
@@ -142,6 +142,7 @@ struct DownloadEventData {
|
||||
|
||||
auto Init() -> bool;
|
||||
void Exit();
|
||||
void ExitSignal();
|
||||
|
||||
// sync functions
|
||||
auto ToMemory(const Api& e) -> ApiResult;
|
||||
|
||||
@@ -1,33 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "fs.hpp"
|
||||
#include "location.hpp"
|
||||
#include "ui/progress_box.hpp"
|
||||
|
||||
#include <switch.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <minizip/ioapi.h>
|
||||
|
||||
namespace sphaira::dump {
|
||||
|
||||
enum DumpLocationType {
|
||||
// dump using native fs.
|
||||
DumpLocationType_SdCard,
|
||||
// dump to usb pc.
|
||||
DumpLocationType_Usb,
|
||||
// dump to usb using tinfoil protocol.
|
||||
DumpLocationType_UsbS2S,
|
||||
// speed test, only reads the data, doesn't write anything.
|
||||
DumpLocationType_DevNull,
|
||||
// dump to stdio, ideal for custom mount points using devoptab, such as hdd.
|
||||
DumpLocationType_Stdio,
|
||||
// dump to custom locations found in locations.ini.
|
||||
DumpLocationType_Network,
|
||||
};
|
||||
|
||||
enum DumpLocationFlag {
|
||||
DumpLocationFlag_SdCard = 1 << DumpLocationType_SdCard,
|
||||
DumpLocationFlag_Usb = 1 << DumpLocationType_Usb,
|
||||
DumpLocationFlag_UsbS2S = 1 << DumpLocationType_UsbS2S,
|
||||
DumpLocationFlag_DevNull = 1 << DumpLocationType_DevNull,
|
||||
DumpLocationFlag_Stdio = 1 << DumpLocationType_Stdio,
|
||||
DumpLocationFlag_Network = 1 << DumpLocationType_Network,
|
||||
DumpLocationFlag_All = DumpLocationFlag_SdCard | DumpLocationFlag_UsbS2S | DumpLocationFlag_DevNull | DumpLocationFlag_Stdio | DumpLocationFlag_Network,
|
||||
DumpLocationFlag_All = DumpLocationFlag_SdCard | DumpLocationFlag_Usb | DumpLocationFlag_UsbS2S | DumpLocationFlag_DevNull | DumpLocationFlag_Stdio,
|
||||
};
|
||||
|
||||
struct DumpEntry {
|
||||
DumpLocationType type;
|
||||
s32 index;
|
||||
};
|
||||
|
||||
struct DumpLocation {
|
||||
DumpEntry entry{};
|
||||
location::StdioEntries stdio{};
|
||||
};
|
||||
|
||||
struct BaseSource {
|
||||
@@ -36,11 +50,36 @@ struct BaseSource {
|
||||
virtual auto GetName(const std::string& path) const -> std::string = 0;
|
||||
virtual auto GetSize(const std::string& path) const -> s64 = 0;
|
||||
virtual auto GetIcon(const std::string& path) const -> int { return 0; }
|
||||
|
||||
Result Read(const std::string& path, void* buf, s64 off, s64 size) {
|
||||
u64 bytes_read;
|
||||
return Read(path, buf, off, size, &bytes_read);
|
||||
}
|
||||
};
|
||||
|
||||
struct WriteSource {
|
||||
virtual ~WriteSource() = default;
|
||||
virtual Result Write(const void* buf, s64 off, s64 size) = 0;
|
||||
virtual Result SetSize(s64 size) = 0;
|
||||
};
|
||||
|
||||
// called after dump has finished.
|
||||
using OnExit = std::function<void(Result rc)>;
|
||||
using OnLocation = std::function<void(const DumpLocation& loc)>;
|
||||
|
||||
void Dump(std::shared_ptr<BaseSource> source, const std::vector<fs::FsPath>& paths, OnExit on_exit = [](Result){}, u32 location_flags = DumpLocationFlag_All);
|
||||
using CustomTransfer = std::function<Result(ui::ProgressBox* pbox, BaseSource* source, WriteSource* writer, const fs::FsPath& path)>;
|
||||
|
||||
// prompts the user to select dump location, calls on_loc on success with the selected location.
|
||||
void DumpGetLocation(const std::string& title, u32 location_flags, const OnLocation& on_loc, const CustomTransfer& custom_transfer = nullptr);
|
||||
|
||||
Result Dump(ui::ProgressBox* pbox, const std::shared_ptr<BaseSource>& source, const DumpLocation& location, const std::vector<fs::FsPath>& paths, const CustomTransfer& custom_transfer = nullptr);
|
||||
|
||||
// dumps to a fetched location using DumpGetLocation().
|
||||
void Dump(const std::shared_ptr<BaseSource>& source, const DumpLocation& location, const std::vector<fs::FsPath>& paths, const OnExit& on_exit, const CustomTransfer& custom_transfer = nullptr);
|
||||
// DumpGetLocation() + Dump() all in one.
|
||||
void Dump(const std::shared_ptr<BaseSource>& source, const std::vector<fs::FsPath>& paths, const OnExit& on_exit = nullptr, u32 location_flags = DumpLocationFlag_All);
|
||||
void Dump(const std::shared_ptr<BaseSource>& source, const std::vector<fs::FsPath>& paths, const CustomTransfer& custom_transfer, const OnExit& on_exit = nullptr, u32 location_flags = DumpLocationFlag_All);
|
||||
|
||||
void FileFuncWriter(WriteSource* writer, zlib_filefunc64_def* funcs);
|
||||
|
||||
} // namespace sphaira::dump
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
#include <nxlink.h>
|
||||
#include <haze.h>
|
||||
#include "download.hpp"
|
||||
|
||||
namespace sphaira::evman {
|
||||
@@ -24,7 +23,6 @@ struct ExitEventData {
|
||||
using EventData = std::variant<
|
||||
LaunchNroEventData,
|
||||
ExitEventData,
|
||||
HazeCallbackData,
|
||||
NxlinkCallbackData,
|
||||
curl::DownloadEventData
|
||||
>;
|
||||
|
||||
@@ -4,18 +4,36 @@
|
||||
#include <dirent.h>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/syslimits.h>
|
||||
#include "defines.hpp"
|
||||
|
||||
namespace fs {
|
||||
|
||||
enum OpenMode : u32 {
|
||||
OpenMode_Read = FsOpenMode_Read,
|
||||
OpenMode_Write = FsOpenMode_Write,
|
||||
OpenMode_Append = FsOpenMode_Append,
|
||||
|
||||
// enables buffering for stdio based files.
|
||||
OpenMode_EnableBuffer = 1 << 16,
|
||||
OpenMode_ReadBuffered = OpenMode_Read | OpenMode_EnableBuffer,
|
||||
OpenMode_WriteBuffered = OpenMode_Write | OpenMode_EnableBuffer,
|
||||
OpenMode_AppendBuffered = OpenMode_Append | OpenMode_EnableBuffer,
|
||||
};
|
||||
|
||||
struct FsPath {
|
||||
FsPath() = default;
|
||||
constexpr FsPath(const auto& str) { From(str); }
|
||||
|
||||
constexpr FsPath(const FsPath& p) { From(p); }
|
||||
constexpr FsPath(const char* str) { From(str); }
|
||||
constexpr FsPath(const std::string& str) { From(str); }
|
||||
constexpr FsPath(const std::string_view& str) { From(str); }
|
||||
|
||||
constexpr void From(const FsPath& p) {
|
||||
*this = p;
|
||||
From(p.s);
|
||||
}
|
||||
|
||||
constexpr void From(const char* str) {
|
||||
@@ -56,6 +74,19 @@ struct FsPath {
|
||||
s[0] = '\0';
|
||||
}
|
||||
|
||||
constexpr auto starts_with(std::string_view str) const -> bool {
|
||||
return !strncasecmp(s, str.data(), str.length());
|
||||
}
|
||||
|
||||
constexpr auto ends_with(std::string_view str) const -> bool {
|
||||
const auto len = length();
|
||||
if (len < str.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !strncasecmp(s + len - str.length(), str.data(), str.length());
|
||||
}
|
||||
|
||||
constexpr operator const char*() const { return s; }
|
||||
constexpr operator char*() { return s; }
|
||||
constexpr operator std::string() { return s; }
|
||||
@@ -65,6 +96,11 @@ struct FsPath {
|
||||
constexpr char& operator[](std::size_t idx) { return s[idx]; }
|
||||
constexpr const char& operator[](std::size_t idx) const { return s[idx]; }
|
||||
|
||||
constexpr FsPath& operator=(const FsPath& p) noexcept {
|
||||
From(p.s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr FsPath operator+(const FsPath& v) const noexcept {
|
||||
FsPath r{*this};
|
||||
return r += v;
|
||||
@@ -116,20 +152,24 @@ struct FsPath {
|
||||
return *this;
|
||||
}
|
||||
|
||||
static constexpr bool path_equal(std::string_view a, std::string_view b) {
|
||||
return a.length() == b.length() && !strncasecmp(a.data(), b.data(), a.length());
|
||||
}
|
||||
|
||||
constexpr bool operator==(const FsPath& v) const noexcept {
|
||||
return !strcasecmp(*this, v);
|
||||
return path_equal(*this, v);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const char* v) const noexcept {
|
||||
return !strcasecmp(*this, v);
|
||||
return path_equal(*this, v);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const std::string& v) const noexcept {
|
||||
return !strncasecmp(*this, v.data(), v.length());
|
||||
return path_equal(*this, v);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const std::string_view v) const noexcept {
|
||||
return !strncasecmp(*this, v.data(), v.length());
|
||||
return path_equal(*this, v);
|
||||
}
|
||||
|
||||
static consteval bool Test(const auto& str) {
|
||||
@@ -142,7 +182,7 @@ struct FsPath {
|
||||
return path[0] == str[0];
|
||||
}
|
||||
|
||||
char s[FS_MAX_PATH]{};
|
||||
char s[PATH_MAX]{};
|
||||
};
|
||||
|
||||
inline FsPath operator+(const char* v, const FsPath& fp) {
|
||||
@@ -160,15 +200,41 @@ inline FsPath operator+(const std::string_view& v, const FsPath& fp) {
|
||||
return r += fp;
|
||||
}
|
||||
|
||||
static_assert(FsPath::Test("abc"));
|
||||
static_assert(FsPath::Test(std::string_view{"abc"}));
|
||||
static_assert(FsPath::Test(std::string{"abc"}));
|
||||
static_assert(FsPath::Test(FsPath{"abc"}));
|
||||
// Fs seems to be limted to file paths of 255 characters.
|
||||
// i've disabled this as network mounts will often have very long paths
|
||||
// that do not have this limit.
|
||||
// a proper fix would be to return an error if the path is too long and the path
|
||||
// is native.
|
||||
struct FsPathReal {
|
||||
static constexpr inline size_t FS_REAL_MAX_LENGTH = PATH_MAX;
|
||||
|
||||
static_assert(FsPath::TestFrom("abc"));
|
||||
static_assert(FsPath::TestFrom(std::string_view{"abc"}));
|
||||
static_assert(FsPath::TestFrom(std::string{"abc"}));
|
||||
static_assert(FsPath::TestFrom(FsPath{"abc"}));
|
||||
constexpr FsPathReal(const FsPath& str) : FsPathReal{str.s} { }
|
||||
explicit constexpr FsPathReal(const char* str) {
|
||||
size_t real = 0;
|
||||
for (size_t i = 0; str[i]; i++) {
|
||||
// skip multiple slashes.
|
||||
if (i && str[i] == '/' && str[i - 1] == '/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// save single char.
|
||||
s[real++] = str[i];
|
||||
|
||||
// check if we have exceeded the path.
|
||||
if (real >= FS_REAL_MAX_LENGTH) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// null the end.
|
||||
s[real] = '\0';
|
||||
}
|
||||
|
||||
constexpr operator const char*() const { return s; }
|
||||
constexpr operator std::string_view() const { return s; }
|
||||
|
||||
char s[PATH_MAX];
|
||||
};
|
||||
|
||||
// fwd
|
||||
struct Fs;
|
||||
@@ -185,16 +251,14 @@ struct File {
|
||||
fs::Fs* m_fs{};
|
||||
FsFile m_native{};
|
||||
std::FILE* m_stdio{};
|
||||
s64 m_stdio_off{};
|
||||
// sadly, fatfs doesn't support fstat, so we have to manually
|
||||
// stat the file to get it's size.
|
||||
FsPath m_path{};
|
||||
u32 m_mode{};
|
||||
};
|
||||
|
||||
struct Dir {
|
||||
~Dir();
|
||||
|
||||
Result GetEntryCount(s64* out);
|
||||
Result Read(s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf);
|
||||
Result ReadAll(std::vector<FsDirectoryEntry>& buf);
|
||||
void Close();
|
||||
|
||||
@@ -206,44 +270,38 @@ struct Dir {
|
||||
|
||||
FsPath AppendPath(const fs::FsPath& root_path, const fs::FsPath& file_path);
|
||||
|
||||
Result CreateFile(FsFileSystem* fs, const FsPath& path, u64 size = 0, u32 option = 0, bool ignore_read_only = true);
|
||||
Result CreateDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result CreateFile(FsFileSystem* fs, const FsPathReal& path, u64 size = 0, u32 option = 0, bool ignore_read_only = true);
|
||||
Result CreateDirectory(FsFileSystem* fs, const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result CreateDirectoryRecursively(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteFile(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectory(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectoryRecursively(FsFileSystem* fs, const FsPath& path, bool ignore_read_only = true);
|
||||
Result RenameFile(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
|
||||
Result RenameDirectory(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
|
||||
Result GetEntryType(FsFileSystem* fs, const FsPath& path, FsDirEntryType* out);
|
||||
Result GetFileTimeStampRaw(FsFileSystem* fs, const FsPath& path, FsTimeStampRaw *out);
|
||||
Result SetTimestamp(FsFileSystem* fs, const FsPath& path, const FsTimeStampRaw* ts);
|
||||
Result DeleteFile(FsFileSystem* fs, const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectory(FsFileSystem* fs, const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectoryRecursively(FsFileSystem* fs, const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result RenameFile(FsFileSystem* fs, const FsPathReal& src, const FsPathReal& dst, bool ignore_read_only = true);
|
||||
Result RenameDirectory(FsFileSystem* fs, const FsPathReal& src, const FsPathReal& dst, bool ignore_read_only = true);
|
||||
Result GetEntryType(FsFileSystem* fs, const FsPathReal& path, FsDirEntryType* out);
|
||||
Result GetFileTimeStampRaw(FsFileSystem* fs, const FsPathReal& path, FsTimeStampRaw *out);
|
||||
Result SetTimestamp(FsFileSystem* fs, const FsPathReal& path, const FsTimeStampRaw* ts);
|
||||
bool FileExists(FsFileSystem* fs, const FsPath& path);
|
||||
bool DirExists(FsFileSystem* fs, const FsPath& path);
|
||||
Result read_entire_file(FsFileSystem* fs, const FsPath& path, std::vector<u8>& out);
|
||||
Result write_entire_file(FsFileSystem* fs, const FsPath& path, const std::vector<u8>& in, bool ignore_read_only = true);
|
||||
Result copy_entire_file(FsFileSystem* fs, const FsPath& dst, const FsPath& src, bool ignore_read_only = true);
|
||||
|
||||
Result CreateFile(const FsPath& path, u64 size = 0, u32 option = 0, bool ignore_read_only = true);
|
||||
Result CreateDirectory(const FsPath& path, bool ignore_read_only = true);
|
||||
Result CreateFile(const FsPathReal& path, u64 size = 0, u32 option = 0, bool ignore_read_only = true);
|
||||
Result CreateDirectory(const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result CreateDirectoryRecursively(const FsPath& path, bool ignore_read_only = true);
|
||||
Result CreateDirectoryRecursivelyWithPath(const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteFile(const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectory(const FsPath& path, bool ignore_read_only = true);
|
||||
Result DeleteFile(const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectory(const FsPathReal& path, bool ignore_read_only = true);
|
||||
Result DeleteDirectoryRecursively(const FsPath& path, bool ignore_read_only = true);
|
||||
Result RenameFile(const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
|
||||
Result RenameDirectory(const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
|
||||
Result GetEntryType(const FsPath& path, FsDirEntryType* out);
|
||||
Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out);
|
||||
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts);
|
||||
Result RenameFile(const FsPathReal& src, const FsPathReal& dst, bool ignore_read_only = true);
|
||||
Result RenameDirectory(const FsPathReal& src, const FsPathReal& dst, bool ignore_read_only = true);
|
||||
Result GetEntryType(const FsPathReal& path, FsDirEntryType* out);
|
||||
Result GetFileTimeStampRaw(const FsPathReal& path, FsTimeStampRaw *out);
|
||||
Result SetTimestamp(const FsPathReal& path, const FsTimeStampRaw* ts);
|
||||
bool FileExists(const FsPath& path);
|
||||
bool DirExists(const FsPath& path);
|
||||
Result read_entire_file(const FsPath& path, std::vector<u8>& out);
|
||||
Result write_entire_file(const FsPath& path, const std::vector<u8>& in, bool ignore_read_only = true);
|
||||
Result copy_entire_file(const FsPath& dst, const FsPath& src, bool ignore_read_only = true);
|
||||
|
||||
Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f);
|
||||
Result OpenDirectory(fs::Fs* fs, const fs::FsPath& path, u32 mode, Dir* d);
|
||||
Result OpenFile(fs::Fs* fs, const FsPathReal& path, u32 mode, File* f);
|
||||
Result OpenDirectory(fs::Fs* fs, const FsPathReal& path, u32 mode, Dir* d);
|
||||
|
||||
// opens dir, fetches count for all entries.
|
||||
// NOTE: this function will be slow on non-native fs, due to multiple
|
||||
@@ -260,23 +318,12 @@ Result DirGetEntryCount(fs::Fs* fs, const fs::FsPath& path, s64* file_count, s64
|
||||
Result FileGetSizeAndTimestamp(fs::Fs* fs, const FsPath& path, FsTimeStampRaw* ts, s64* size);
|
||||
Result IsDirEmpty(fs::Fs* m_fs, const fs::FsPath& path, bool* out);
|
||||
|
||||
struct Fs {
|
||||
static constexpr inline u32 FsModule = 505;
|
||||
static constexpr inline Result ResultTooManyEntries = MAKERESULT(FsModule, 1);
|
||||
static constexpr inline Result ResultNewPathTooLarge = MAKERESULT(FsModule, 2);
|
||||
static constexpr inline Result ResultInvalidType = MAKERESULT(FsModule, 3);
|
||||
static constexpr inline Result ResultEmpty = MAKERESULT(FsModule, 4);
|
||||
static constexpr inline Result ResultAlreadyRoot = MAKERESULT(FsModule, 5);
|
||||
static constexpr inline Result ResultNoCurrentPath = MAKERESULT(FsModule, 6);
|
||||
static constexpr inline Result ResultBrokenCurrentPath = MAKERESULT(FsModule, 7);
|
||||
static constexpr inline Result ResultIndexOutOfBounds = MAKERESULT(FsModule, 8);
|
||||
static constexpr inline Result ResultFsNotActive = MAKERESULT(FsModule, 9);
|
||||
static constexpr inline Result ResultNewPathEmpty = MAKERESULT(FsModule, 10);
|
||||
static constexpr inline Result ResultLoadingCancelled = MAKERESULT(FsModule, 11);
|
||||
static constexpr inline Result ResultBrokenRoot = MAKERESULT(FsModule, 12);
|
||||
static constexpr inline Result ResultUnknownStdioError = MAKERESULT(FsModule, 13);
|
||||
static constexpr inline Result ResultReadOnly = MAKERESULT(FsModule, 14);
|
||||
// helpers.
|
||||
Result read_entire_file(Fs* fs, const FsPath& path, std::vector<u8>& out);
|
||||
Result write_entire_file(Fs* fs, const FsPath& path, std::span<const u8> in, bool ignore_read_only = true);
|
||||
Result copy_entire_file(Fs* fs, const FsPath& dst, const FsPath& src, bool ignore_read_only = true);
|
||||
|
||||
struct Fs {
|
||||
Fs(bool ignore_read_only = true) : m_ignore_read_only{ignore_read_only} {}
|
||||
virtual ~Fs() = default;
|
||||
|
||||
@@ -292,13 +339,12 @@ struct Fs {
|
||||
virtual Result GetEntryType(const FsPath& path, FsDirEntryType* out) = 0;
|
||||
virtual Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) = 0;
|
||||
virtual Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) = 0;
|
||||
virtual Result Commit() = 0;
|
||||
virtual bool FileExists(const FsPath& path) = 0;
|
||||
virtual bool DirExists(const FsPath& path) = 0;
|
||||
virtual bool IsNative() const = 0;
|
||||
virtual bool IsSd() const { return false; }
|
||||
virtual FsPath Root() const { return "/"; }
|
||||
virtual Result read_entire_file(const FsPath& path, std::vector<u8>& out) = 0;
|
||||
virtual Result write_entire_file(const FsPath& path, const std::vector<u8>& in) = 0;
|
||||
virtual Result copy_entire_file(const FsPath& dst, const FsPath& src) = 0;
|
||||
|
||||
Result OpenFile(const fs::FsPath& path, u32 mode, File* f) {
|
||||
return fs::OpenFile(this, path, mode, f);
|
||||
@@ -318,6 +364,15 @@ struct Fs {
|
||||
Result IsDirEmpty(const fs::FsPath& path, bool* out) {
|
||||
return fs::IsDirEmpty(this, path, out);
|
||||
}
|
||||
Result read_entire_file(const FsPath& path, std::vector<u8>& out) {
|
||||
return fs::read_entire_file(this, path, out);
|
||||
}
|
||||
Result write_entire_file(const FsPath& path, std::span<const u8> in) {
|
||||
return fs::write_entire_file(this, path, in, m_ignore_read_only);
|
||||
}
|
||||
Result copy_entire_file(const FsPath& dst, const FsPath& src) {
|
||||
return fs::copy_entire_file(this, dst, src, m_ignore_read_only);
|
||||
}
|
||||
|
||||
void SetIgnoreReadOnly(bool enable) {
|
||||
m_ignore_read_only = enable;
|
||||
@@ -367,6 +422,9 @@ struct FsStdio : Fs {
|
||||
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
||||
return fs::SetTimestamp(path, ts);
|
||||
}
|
||||
Result Commit() override {
|
||||
R_SUCCEED();
|
||||
}
|
||||
bool FileExists(const FsPath& path) override {
|
||||
return fs::FileExists(path);
|
||||
}
|
||||
@@ -379,15 +437,6 @@ struct FsStdio : Fs {
|
||||
FsPath Root() const override {
|
||||
return m_root;
|
||||
}
|
||||
Result read_entire_file(const FsPath& path, std::vector<u8>& out) override {
|
||||
return fs::read_entire_file(path, out);
|
||||
}
|
||||
Result write_entire_file(const FsPath& path, const std::vector<u8>& in) override {
|
||||
return fs::write_entire_file(path, in, m_ignore_read_only);
|
||||
}
|
||||
Result copy_entire_file(const FsPath& dst, const FsPath& src) override {
|
||||
return fs::copy_entire_file(dst, src, m_ignore_read_only);
|
||||
}
|
||||
|
||||
const FsPath m_root;
|
||||
};
|
||||
@@ -402,10 +451,6 @@ struct FsNative : Fs {
|
||||
}
|
||||
}
|
||||
|
||||
Result Commit() {
|
||||
return fsFsCommit(&m_fs);
|
||||
}
|
||||
|
||||
Result GetFreeSpace(const FsPath& path, s64* out) {
|
||||
return fsFsGetFreeSpace(&m_fs, path, out);
|
||||
}
|
||||
@@ -414,36 +459,6 @@ struct FsNative : Fs {
|
||||
return fsFsGetTotalSpace(&m_fs, path, out);
|
||||
}
|
||||
|
||||
// Result OpenDirectory(const FsPath& path, u32 mode, FsDir *out) {
|
||||
// return fsFsOpenDirectory(&m_fs, path, mode, out);
|
||||
// }
|
||||
|
||||
// void DirClose(FsDir *d) {
|
||||
// fsDirClose(d);
|
||||
// }
|
||||
|
||||
// Result DirGetEntryCount(FsDir *d, s64* out) {
|
||||
// return fsDirGetEntryCount(d, out);
|
||||
// }
|
||||
|
||||
// Result DirGetEntryCount(const FsPath& path, u32 mode, s64* out) {
|
||||
// FsDir d;
|
||||
// R_TRY(OpenDirectory(path, mode, &d));
|
||||
// ON_SCOPE_EXIT(DirClose(&d));
|
||||
// return DirGetEntryCount(&d, out);
|
||||
// }
|
||||
|
||||
// Result DirRead(FsDir *d, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
|
||||
// return fsDirRead(d, total_entries, max_entries, buf);
|
||||
// }
|
||||
|
||||
// Result DirRead(const FsPath& path, u32 mode, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
|
||||
// FsDir d;
|
||||
// R_TRY(OpenDirectory(path, mode, &d));
|
||||
// ON_SCOPE_EXIT(DirClose(&d));
|
||||
// return DirRead(&d, total_entries, max_entries, buf);
|
||||
// }
|
||||
|
||||
virtual bool IsFsActive() {
|
||||
return serviceIsActive(&m_fs.s);
|
||||
}
|
||||
@@ -488,6 +503,9 @@ struct FsNative : Fs {
|
||||
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
|
||||
return fs::SetTimestamp(&m_fs, path, ts);
|
||||
}
|
||||
Result Commit() override {
|
||||
return fsFsCommit(&m_fs);
|
||||
}
|
||||
bool FileExists(const FsPath& path) override {
|
||||
return fs::FileExists(&m_fs, path);
|
||||
}
|
||||
@@ -497,19 +515,10 @@ struct FsNative : Fs {
|
||||
bool IsNative() const override {
|
||||
return true;
|
||||
}
|
||||
Result read_entire_file(const FsPath& path, std::vector<u8>& out) override {
|
||||
return fs::read_entire_file(&m_fs, path, out);
|
||||
}
|
||||
Result write_entire_file(const FsPath& path, const std::vector<u8>& in) override {
|
||||
return fs::write_entire_file(&m_fs, path, in, m_ignore_read_only);
|
||||
}
|
||||
Result copy_entire_file(const FsPath& dst, const FsPath& src) override {
|
||||
return fs::copy_entire_file(&m_fs, dst, src, m_ignore_read_only);
|
||||
}
|
||||
|
||||
FsFileSystem m_fs{};
|
||||
Result m_open_result{};
|
||||
bool m_own{true};
|
||||
const bool m_own{true};
|
||||
};
|
||||
|
||||
#if 0
|
||||
@@ -523,31 +532,53 @@ struct FsNativeSd final : FsNative {
|
||||
FsNativeSd(bool ignore_read_only = true) : FsNative{fsdevGetDeviceFileSystem("sdmc:"), false, ignore_read_only} {
|
||||
m_open_result = 0;
|
||||
}
|
||||
|
||||
bool IsSd() const override { return true; }
|
||||
};
|
||||
#endif
|
||||
|
||||
struct FsNativeBis final : FsNative {
|
||||
FsNativeBis(FsBisPartitionId id, const FsPath& string, bool ignore_read_only = true) : FsNative{ignore_read_only} {
|
||||
FsNativeBis(FsBisPartitionId id, const FsPath& string) {
|
||||
m_open_result = fsOpenBisFileSystem(&m_fs, id, string);
|
||||
}
|
||||
};
|
||||
|
||||
struct FsNativeImage final : FsNative {
|
||||
FsNativeImage(FsImageDirectoryId id, bool ignore_read_only = true) : FsNative{ignore_read_only} {
|
||||
FsNativeImage(FsImageDirectoryId id) {
|
||||
m_open_result = fsOpenImageDirectoryFileSystem(&m_fs, id);
|
||||
}
|
||||
};
|
||||
|
||||
struct FsNativeContentStorage final : FsNative {
|
||||
FsNativeContentStorage(FsContentStorageId id, bool ignore_read_only = true) : FsNative{ignore_read_only} {
|
||||
FsNativeContentStorage(FsContentStorageId id) {
|
||||
m_open_result = fsOpenContentStorageFileSystem(&m_fs, id);
|
||||
}
|
||||
};
|
||||
|
||||
struct FsNativeGameCard final : FsNative {
|
||||
FsNativeGameCard(const FsGameCardHandle* handle, FsGameCardPartition partition, bool ignore_read_only = true) : FsNative{ignore_read_only} {
|
||||
FsNativeGameCard(const FsGameCardHandle* handle, FsGameCardPartition partition) {
|
||||
m_open_result = fsOpenGameCardFileSystem(&m_fs, handle, partition);
|
||||
}
|
||||
};
|
||||
|
||||
struct FsNativeSave final : FsNative {
|
||||
FsNativeSave(FsSaveDataType data_type, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr, bool read_only) {
|
||||
if (data_type == FsSaveDataType_System || data_type == FsSaveDataType_SystemBcat) {
|
||||
m_open_result = fsOpenSaveDataFileSystemBySystemSaveDataId(&m_fs, FsSaveDataSpaceId_System, attr);
|
||||
} else {
|
||||
if (read_only) {
|
||||
m_open_result = fsOpenReadOnlySaveDataFileSystem(&m_fs, save_data_space_id, attr);
|
||||
} else {
|
||||
m_open_result = fsOpenSaveDataFileSystem(&m_fs, save_data_space_id, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FsNativeId final : FsNative {
|
||||
FsNativeId(u64 program_id, FsFileSystemType type, const FsPath& path, FsContentAttributes attr = FsContentAttributes_All) {
|
||||
m_open_result = fsOpenFileSystemWithId(&m_fs, program_id, type, path, attr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
@@ -6,12 +6,13 @@ namespace sphaira::ftpsrv {
|
||||
|
||||
bool Init();
|
||||
void Exit();
|
||||
void ExitSignal();
|
||||
|
||||
using OnInstallStart = std::function<bool(void* user, const char* path)>;
|
||||
using OnInstallWrite = std::function<bool(void* user, const void* buf, size_t size)>;
|
||||
using OnInstallClose = std::function<void(void* user)>;
|
||||
using OnInstallStart = std::function<bool(const char* path)>;
|
||||
using OnInstallWrite = std::function<bool(const void* buf, size_t size)>;
|
||||
using OnInstallClose = std::function<void()>;
|
||||
|
||||
void InitInstallMode(void* user, OnInstallStart on_start, OnInstallWrite on_write, OnInstallClose on_close);
|
||||
void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close);
|
||||
void DisableInstallMode();
|
||||
|
||||
unsigned GetPort();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ui/progress_box.hpp"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::hash {
|
||||
@@ -13,6 +14,7 @@ enum class Type {
|
||||
Md5,
|
||||
Sha1,
|
||||
Sha256,
|
||||
Null,
|
||||
};
|
||||
|
||||
struct BaseSource {
|
||||
@@ -24,7 +26,8 @@ struct BaseSource {
|
||||
auto GetTypeStr(Type type) -> const char*;
|
||||
|
||||
// returns the hash string.
|
||||
Result Hash(ui::ProgressBox* pbox, Type type, std::shared_ptr<BaseSource> source, std::string& out);
|
||||
Result Hash(ui::ProgressBox* pbox, Type type, BaseSource* source, std::string& out);
|
||||
Result Hash(ui::ProgressBox* pbox, Type type, fs::Fs* fs, const fs::FsPath& path, std::string& out);
|
||||
Result Hash(ui::ProgressBox* pbox, Type type, std::span<const u8> data, std::string& out);
|
||||
|
||||
} // namespace sphaira::hash
|
||||
|
||||
18
sphaira/include/haze_helper.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace sphaira::libhaze {
|
||||
|
||||
bool Init();
|
||||
bool IsInit();
|
||||
void Exit();
|
||||
|
||||
using OnInstallStart = std::function<bool(const char* path)>;
|
||||
using OnInstallWrite = std::function<bool(const void* buf, size_t size)>;
|
||||
using OnInstallClose = std::function<void()>;
|
||||
|
||||
void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close);
|
||||
void DisableInstallMode();
|
||||
|
||||
} // namespace sphaira::libhaze
|
||||
@@ -12,8 +12,14 @@ struct ImageResult {
|
||||
int w, h;
|
||||
};
|
||||
|
||||
auto ImageLoadFromMemory(std::span<const u8> data) -> ImageResult;
|
||||
auto ImageLoadFromFile(const fs::FsPath& file) -> ImageResult;
|
||||
enum ImageFlag {
|
||||
ImageFlag_None = 0,
|
||||
// set this if the image is a jpeg, will use oss-nvjpg to load.
|
||||
ImageFlag_JPEG = 1 << 0,
|
||||
};
|
||||
|
||||
auto ImageLoadFromMemory(std::span<const u8> data, u32 flags = ImageFlag_None) -> ImageResult;
|
||||
auto ImageLoadFromFile(const fs::FsPath& file, u32 flags = ImageFlag_None) -> ImageResult;
|
||||
auto ImageResize(std::span<const u8> data, int inx, int iny, int outx, int outy) -> ImageResult;
|
||||
auto ImageConvertToJpg(std::span<const u8> data, int x, int y) -> ImageResult;
|
||||
|
||||
|
||||
@@ -3,23 +3,13 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <switch.h>
|
||||
// to import FsEntryFlags.
|
||||
// todo: this should be part of a smaller header, such as filesystem_types.hpp
|
||||
#include "ui/menus/filebrowser.hpp"
|
||||
|
||||
namespace sphaira::location {
|
||||
|
||||
struct Entry {
|
||||
std::string name{};
|
||||
std::string url{};
|
||||
std::string user{};
|
||||
std::string pass{};
|
||||
std::string bearer{};
|
||||
std::string pub_key{};
|
||||
std::string priv_key{};
|
||||
u16 port{};
|
||||
};
|
||||
using Entries = std::vector<Entry>;
|
||||
|
||||
auto Load() -> Entries;
|
||||
void Add(const Entry& e);
|
||||
using FsEntryFlag = ui::menu::filebrowser::FsEntryFlag;
|
||||
|
||||
// helper for hdd devices.
|
||||
// this doesn't really belong in this header, however
|
||||
@@ -29,8 +19,14 @@ struct StdioEntry {
|
||||
std::string mount{};
|
||||
// ums0: (USB Flash Disk)
|
||||
std::string name{};
|
||||
// set if read-only.
|
||||
bool write_protect;
|
||||
// FsEntryFlag
|
||||
u32 flags{};
|
||||
// optional dump path inside the mount point.
|
||||
std::string dump_path{};
|
||||
// set to hide for filebrowser.
|
||||
bool fs_hidden{};
|
||||
// set to hide in dump list.
|
||||
bool dump_hidden{};
|
||||
};
|
||||
|
||||
using StdioEntries = std::vector<StdioEntry>;
|
||||
|
||||
@@ -12,6 +12,8 @@ extern "C" {
|
||||
bool log_file_init();
|
||||
bool log_nxlink_init();
|
||||
void log_file_exit();
|
||||
bool log_is_init();
|
||||
|
||||
void log_nxlink_exit();
|
||||
void log_write(const char* s, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
void log_write_arg(const char* s, va_list* v);
|
||||
|
||||
32
sphaira/include/minizip_helper.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <minizip/ioapi.h>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <switch.h>
|
||||
#include "fs.hpp"
|
||||
|
||||
namespace sphaira::mz {
|
||||
|
||||
struct MzMem {
|
||||
std::vector<u8> buf;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
struct MzSpan {
|
||||
std::span<const u8> buf;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
void FileFuncMem(MzMem* mem, zlib_filefunc64_def* funcs);
|
||||
void FileFuncSpan(MzSpan* span, zlib_filefunc64_def* funcs);
|
||||
void FileFuncStdio(zlib_filefunc64_def* funcs);
|
||||
void FileFuncNative(zlib_filefunc64_def* funcs);
|
||||
|
||||
// minizip takes 18ms to open a zip and 4ms to parse the first file entry.
|
||||
// this results in a dropped frame.
|
||||
// this version simply reads the local header + file name in 2 reads,
|
||||
// which takes 1-2ms.
|
||||
Result PeekFirstFileName(fs::Fs* fs, const fs::FsPath& path, fs::FsPath& name);
|
||||
|
||||
} // namespace sphaira::mz
|
||||
@@ -9,8 +9,14 @@
|
||||
|
||||
namespace sphaira {
|
||||
|
||||
struct NroData {
|
||||
NroStart start;
|
||||
NroHeader header;
|
||||
};
|
||||
|
||||
struct Hbini {
|
||||
u64 timestamp{}; // timestamp of last launch
|
||||
bool hidden{};
|
||||
};
|
||||
|
||||
struct MiniNacp {
|
||||
@@ -61,7 +67,7 @@ auto nro_parse(const fs::FsPath& path, NroEntry& entry) -> Result;
|
||||
* nro found.
|
||||
* this does nothing if nested=false.
|
||||
*/
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_spahira, bool nested = true, bool scan_all_dir = true) -> Result;
|
||||
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool nested = true, bool scan_all_dir = true) -> Result;
|
||||
|
||||
auto nro_get_icon(const fs::FsPath& path, u64 size, u64 offset) -> std::vector<u8>;
|
||||
auto nro_get_icon(const fs::FsPath& path) -> std::vector<u8>;
|
||||
|
||||
@@ -44,6 +44,9 @@ bool nxlinkInitialize(NxlinkCallback callback);
|
||||
// signal for the event to close and then join the thread.
|
||||
void nxlinkExit();
|
||||
|
||||
// async the exit, call this first and then call exit later to avoid blocking.
|
||||
void nxlinkSignalExit();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -7,10 +7,11 @@ namespace sphaira::option {
|
||||
|
||||
template<typename T>
|
||||
struct OptionBase {
|
||||
OptionBase(const std::string& section, const std::string& name, T default_value)
|
||||
OptionBase(const std::string& section, const std::string& name, T default_value, bool file = true)
|
||||
: m_section{section}
|
||||
, m_name{name}
|
||||
, m_default_value{default_value}
|
||||
, m_file{file}
|
||||
{}
|
||||
|
||||
auto Get() -> T;
|
||||
@@ -29,11 +30,13 @@ private:
|
||||
const std::string m_section;
|
||||
const std::string m_name;
|
||||
const T m_default_value;
|
||||
const bool m_file;
|
||||
std::optional<T> m_value;
|
||||
};
|
||||
|
||||
using OptionBool = OptionBase<bool>;
|
||||
using OptionLong = OptionBase<long>;
|
||||
using OptionFloat = OptionBase<float>;
|
||||
using OptionString = OptionBase<std::string>;
|
||||
|
||||
} // namespace sphaira::option
|
||||
|
||||
@@ -20,17 +20,6 @@ struct OwoConfig {
|
||||
std::vector<u8> program_nca{};
|
||||
};
|
||||
|
||||
enum {
|
||||
Module_Owo = 424,
|
||||
};
|
||||
|
||||
enum OwoError {
|
||||
OwoError_BadArgs = MAKERESULT(Module_Owo, 1),
|
||||
};
|
||||
|
||||
// fwd
|
||||
// struct ui::ProgressBox;
|
||||
|
||||
auto install_forwarder(OwoConfig& config, NcmStorageId storage_id) -> Result;
|
||||
auto install_forwarder(ui::ProgressBox* pbox, OwoConfig& config, NcmStorageId storage_id) -> Result;
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include <switch.h>
|
||||
#include <string>
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
namespace sphaira::swkbd {
|
||||
|
||||
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, const char* initial = nullptr, s64 len_min = -1, s64 len_max = FS_MAX_PATH);
|
||||
Result ShowText(std::string& out, const char* header = nullptr, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = PATH_MAX);
|
||||
Result ShowNumPad(s64& out, const char* header = nullptr, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = PATH_MAX);
|
||||
|
||||
} // namespace sphaira::swkbd
|
||||
|
||||
@@ -6,7 +6,19 @@
|
||||
|
||||
namespace sphaira::thread {
|
||||
|
||||
enum class Mode {
|
||||
// default, always multi-thread.
|
||||
MultiThreaded,
|
||||
// always single-thread.
|
||||
SingleThreaded,
|
||||
// check buffer size, if smaller, single thread.
|
||||
SingleThreadedIfSmaller,
|
||||
};
|
||||
|
||||
using DecompressWriteCallback = std::function<Result(const void* data, s64 size)>;
|
||||
|
||||
using ReadCallback = std::function<Result(void* data, s64 off, s64 size, u64* bytes_read)>;
|
||||
using DecompressCallback = std::function<Result(void* data, s64 off, s64 size, const DecompressWriteCallback& callback)>;
|
||||
using WriteCallback = std::function<Result(const void* data, s64 off, s64 size)>;
|
||||
|
||||
// used for pull api
|
||||
@@ -23,10 +35,26 @@ using StartCallback = std::function<Result(PullCallback pull)>;
|
||||
using StartCallback2 = std::function<Result(StartThreadCallback start, PullCallback pull)>;
|
||||
|
||||
// reads data from rfunc into wfunc.
|
||||
Result Transfer(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, WriteCallback wfunc);
|
||||
Result Transfer(ui::ProgressBox* pbox, s64 size, const ReadCallback& rfunc, const WriteCallback& wfunc, Mode mode = Mode::MultiThreaded);
|
||||
Result Transfer(ui::ProgressBox* pbox, s64 size, const ReadCallback& rfunc, const DecompressCallback& dfunc, const WriteCallback& wfunc, Mode mode = Mode::MultiThreaded);
|
||||
|
||||
// reads data from rfunc, pull data from provided pull() callback.
|
||||
Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback sfunc);
|
||||
Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback2 sfunc);
|
||||
Result TransferPull(ui::ProgressBox* pbox, s64 size, const ReadCallback& rfunc, const StartCallback& sfunc, Mode mode = Mode::MultiThreaded);
|
||||
Result TransferPull(ui::ProgressBox* pbox, s64 size, const ReadCallback& rfunc, const StartCallback2& sfunc, Mode mode = Mode::MultiThreaded);
|
||||
|
||||
// helper for extract zips.
|
||||
// this will multi-thread unzip if size >= 512KiB, otherwise it'll single pass.
|
||||
Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32 = 0, Mode mode = Mode::SingleThreadedIfSmaller);
|
||||
|
||||
// same as above but for zipping files.
|
||||
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32 = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
|
||||
|
||||
// passes the name inside the zip an final output path.
|
||||
using UnzipAllFilter = std::function<bool(const fs::FsPath& name, fs::FsPath& path)>;
|
||||
|
||||
// helper all-in-one unzip function that unzips a zip (either open or path provided).
|
||||
// the filter function can be used to modify the path and filter out unwanted files.
|
||||
Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& base_path, const UnzipAllFilter& filter = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
|
||||
Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, const UnzipAllFilter& filter = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
|
||||
|
||||
} // namespace sphaira::thread
|
||||
|
||||
83
sphaira/include/title_info.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::title {
|
||||
|
||||
constexpr u32 ContentMetaTypeToContentFlag(u8 meta_type) {
|
||||
if (meta_type & 0x80) {
|
||||
return 1 << (meta_type - 0x80);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum ContentFlag {
|
||||
ContentFlag_Application = ContentMetaTypeToContentFlag(NcmContentMetaType_Application),
|
||||
ContentFlag_Patch = ContentMetaTypeToContentFlag(NcmContentMetaType_Patch),
|
||||
ContentFlag_AddOnContent = ContentMetaTypeToContentFlag(NcmContentMetaType_AddOnContent),
|
||||
ContentFlag_DataPatch = ContentMetaTypeToContentFlag(NcmContentMetaType_DataPatch),
|
||||
|
||||
// nca locations where a control.nacp can exist.
|
||||
ContentFlag_Nacp = ContentFlag_Application | ContentFlag_Patch,
|
||||
// all of the above.
|
||||
ContentFlag_All = ContentFlag_Application | ContentFlag_Patch | ContentFlag_AddOnContent | ContentFlag_DataPatch,
|
||||
};
|
||||
|
||||
enum class NacpLoadStatus {
|
||||
// not yet attempted to be loaded.
|
||||
None,
|
||||
// started loading.
|
||||
Progress,
|
||||
// loaded, ready to parse.
|
||||
Loaded,
|
||||
// failed to load, do not attempt to load again!
|
||||
Error,
|
||||
};
|
||||
|
||||
struct ThreadResultData {
|
||||
u64 id{};
|
||||
std::vector<u8> icon;
|
||||
NacpLanguageEntry lang{};
|
||||
NacpLoadStatus status{NacpLoadStatus::None};
|
||||
};
|
||||
|
||||
using MetaEntries = std::vector<NsApplicationContentMetaStatus>;
|
||||
|
||||
// starts background thread (ref counted).
|
||||
Result Init();
|
||||
// closes the background thread.
|
||||
void Exit();
|
||||
// clears cache and empties the result array.
|
||||
void Clear();
|
||||
|
||||
// adds new entry to queue.
|
||||
void PushAsync(u64 app_id);
|
||||
void PushAsync(const std::span<const NsApplicationRecord> app_ids);
|
||||
// gets entry without removing it from the queue.
|
||||
auto GetAsync(u64 app_id) -> ThreadResultData*;
|
||||
// single threaded title info fetch.
|
||||
auto Get(u64 app_id, bool* cached = nullptr) -> ThreadResultData*;
|
||||
|
||||
auto GetNcmCs(u8 storage_id) -> NcmContentStorage&;
|
||||
auto GetNcmDb(u8 storage_id) -> NcmContentMetaDatabase&;
|
||||
|
||||
// gets all meta entries for an id.
|
||||
Result GetMetaEntries(u64 id, MetaEntries& out, u32 flags = ContentFlag_All);
|
||||
|
||||
// returns the nca path of a control nca.
|
||||
Result GetControlPathFromStatus(const NsApplicationContentMetaStatus& status, u64* out_program_id, fs::FsPath* out_path);
|
||||
|
||||
// taken from nxdumptool.
|
||||
void utilsReplaceIllegalCharacters(char *str, bool ascii_only);
|
||||
|
||||
// /atmosphere/contents/xxx
|
||||
auto GetContentsPath(u64 app_id) -> fs::FsPath;
|
||||
|
||||
} // namespace sphaira::title
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
private:
|
||||
std::optional<Result> m_code{};
|
||||
std::string m_message{};
|
||||
std::string m_code_message{};
|
||||
std::string m_code_module{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -10,14 +10,14 @@ struct List final : Object {
|
||||
GRID,
|
||||
};
|
||||
|
||||
using Callback = std::function<void(NVGcontext* vg, Theme* theme, Vec4 v, s64 index)>;
|
||||
using Callback = std::function<void(NVGcontext* vg, Theme* theme, const Vec4& v, s64 index)>;
|
||||
using TouchCallback = std::function<void(bool touch, s64 index)>;
|
||||
|
||||
List(s64 row, s64 page, const Vec4& pos, const Vec4& v, const Vec2& pad = {});
|
||||
|
||||
void OnUpdate(Controller* controller, TouchInfo* touch, s64 index, s64 count, TouchCallback callback);
|
||||
void OnUpdate(Controller* controller, TouchInfo* touch, s64 index, s64 count, const TouchCallback& callback);
|
||||
|
||||
void Draw(NVGcontext* vg, Theme* theme, s64 count, Callback callback) const;
|
||||
void Draw(NVGcontext* vg, Theme* theme, s64 count, const Callback& callback) const;
|
||||
|
||||
auto SetScrollBarPos(float x, float y, float h) {
|
||||
m_scrollbar.x = x;
|
||||
@@ -73,10 +73,10 @@ private:
|
||||
auto ClampX(float x, s64 count) const -> float;
|
||||
auto ClampY(float y, s64 count) const -> float;
|
||||
|
||||
void OnUpdateHome(Controller* controller, TouchInfo* touch, s64 index, s64 count, TouchCallback callback);
|
||||
void OnUpdateGrid(Controller* controller, TouchInfo* touch, s64 index, s64 count, TouchCallback callback);
|
||||
void DrawHome(NVGcontext* vg, Theme* theme, s64 count, Callback callback) const;
|
||||
void DrawGrid(NVGcontext* vg, Theme* theme, s64 count, Callback callback) const;
|
||||
void OnUpdateHome(Controller* controller, TouchInfo* touch, s64 index, s64 count, const TouchCallback& callback);
|
||||
void OnUpdateGrid(Controller* controller, TouchInfo* touch, s64 index, s64 count, const TouchCallback& callback);
|
||||
void DrawHome(NVGcontext* vg, Theme* theme, s64 count, const Callback& callback) const;
|
||||
void DrawGrid(NVGcontext* vg, Theme* theme, s64 count, const Callback& callback) const;
|
||||
|
||||
private:
|
||||
const s64 m_row;
|
||||
|
||||
@@ -86,14 +86,16 @@ struct EntryMenu final : MenuBase {
|
||||
|
||||
private:
|
||||
struct Option {
|
||||
Option(const std::string& dt, const std::string& ct, std::function<void(void)> f)
|
||||
using Callback = std::function<void(void)>;
|
||||
|
||||
Option(const std::string& dt, const std::string& ct, const Callback& f)
|
||||
: display_text{dt}, confirm_text{ct}, func{f} {}
|
||||
Option(const std::string& dt, std::function<void(void)> f)
|
||||
Option(const std::string& dt, const Callback& f)
|
||||
: display_text{dt}, func{f} {}
|
||||
|
||||
std::string display_text{};
|
||||
std::string confirm_text{};
|
||||
std::function<void(void)> func{};
|
||||
const std::string display_text;
|
||||
const std::string confirm_text;
|
||||
const Callback func{};
|
||||
};
|
||||
|
||||
Entry& m_entry;
|
||||
@@ -105,11 +107,14 @@ private:
|
||||
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::unique_ptr<ScrollableText> m_details{};
|
||||
std::unique_ptr<ScrollableText> m_changelog{};
|
||||
ScrollableText* m_detail_changelog{};
|
||||
std::unique_ptr<ScrollableText> m_manifest_list{};
|
||||
|
||||
bool m_show_changlog{};
|
||||
bool m_show_file_list{};
|
||||
ImageDownloadState m_file_list_state{};
|
||||
};
|
||||
|
||||
enum Filter {
|
||||
|
||||
19
sphaira/include/ui/menus/file_picker.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/filebrowser.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::filebrowser::picker {
|
||||
|
||||
using Callback = std::function<bool(const fs::FsPath& path)>;
|
||||
|
||||
struct Menu final : Base {
|
||||
explicit Menu(const Callback& cb, const std::vector<std::string>& filter = {}, const fs::FsPath& path = {});
|
||||
|
||||
private:
|
||||
void OnClick(FsView* view, const FsEntry& fs_entry, const FileEntry& entry, const fs::FsPath& path) override;
|
||||
|
||||
private:
|
||||
const Callback m_callback;
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::filebrowser::picker
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace sphaira::ui::menu::fileview {
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
Menu(const fs::FsPath& path);
|
||||
Menu(fs::Fs* fs, const fs::FsPath& path);
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "File"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
@@ -15,8 +15,8 @@ struct Menu final : MenuBase {
|
||||
void OnFocusGained() override;
|
||||
|
||||
private:
|
||||
fs::Fs* const m_fs;
|
||||
const fs::FsPath m_path;
|
||||
fs::FsNativeSd m_fs{};
|
||||
fs::File m_file{};
|
||||
s64 m_file_size{};
|
||||
s64 m_file_offset{};
|
||||
|
||||
@@ -2,21 +2,48 @@
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
#include "ui/scrolling_text.hpp"
|
||||
#include "ui/progress_box.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "option.hpp"
|
||||
#include "hasher.hpp"
|
||||
// #include <optional>
|
||||
#include "nro.hpp"
|
||||
#include <span>
|
||||
|
||||
namespace sphaira::ui::menu::filebrowser {
|
||||
|
||||
enum FsOption : u32 {
|
||||
FsOption_NONE,
|
||||
|
||||
// can split screen.
|
||||
FsOption_CanSplit = BIT(0),
|
||||
// can selected multiple files.
|
||||
FsOption_CanSelect = BIT(1),
|
||||
// shows the option to install.
|
||||
FsOption_CanInstall = BIT(2),
|
||||
// loads file assoc.
|
||||
FsOption_LoadAssoc = BIT(3),
|
||||
// do not prompt on exit even if not tabbed.
|
||||
FsOption_DoNotPrompt = BIT(4),
|
||||
|
||||
FsOption_Normal = FsOption_LoadAssoc | FsOption_CanInstall | FsOption_CanSplit | FsOption_CanSelect,
|
||||
FsOption_All = FsOption_DoNotPrompt | FsOption_Normal,
|
||||
FsOption_Picker = FsOption_NONE,
|
||||
};
|
||||
|
||||
enum FsEntryFlag {
|
||||
FsEntryFlag_None,
|
||||
// write protected.
|
||||
FsEntryFlag_ReadOnly = 1 << 0,
|
||||
// supports file assoc.
|
||||
FsEntryFlag_Assoc = 1 << 1,
|
||||
// this is an sd card, files can be launched from here.
|
||||
FsEntryFlag_IsSd = 1 << 2, // todo: remove this.
|
||||
// do not stat files in this entry (faster for network mount).
|
||||
FsEntryFlag_NoStatFile = 1 << 3,
|
||||
FsEntryFlag_NoStatDir = 1 << 4,
|
||||
FsEntryFlag_NoRandomReads = 1 << 5,
|
||||
FsEntryFlag_NoRandomWrites = 1 << 6,
|
||||
};
|
||||
|
||||
enum class FsType {
|
||||
@@ -24,6 +51,7 @@ enum class FsType {
|
||||
ImageNand,
|
||||
ImageSd,
|
||||
Stdio,
|
||||
Custom,
|
||||
};
|
||||
|
||||
enum class SelectedType {
|
||||
@@ -62,13 +90,33 @@ struct FsEntry {
|
||||
return flags & FsEntryFlag_Assoc;
|
||||
}
|
||||
|
||||
auto IsSd() const -> bool {
|
||||
return flags & FsEntryFlag_IsSd;
|
||||
}
|
||||
|
||||
auto IsNoStatFile() const -> bool {
|
||||
return flags & FsEntryFlag_NoStatFile;
|
||||
}
|
||||
|
||||
auto IsNoStatDir() const -> bool {
|
||||
return flags & FsEntryFlag_NoStatDir;
|
||||
}
|
||||
|
||||
auto IsNoRandomReads() const -> bool {
|
||||
return flags & FsEntryFlag_NoRandomReads;
|
||||
}
|
||||
|
||||
auto IsNoRandomWrites() const -> bool {
|
||||
return flags & FsEntryFlag_NoRandomWrites;
|
||||
}
|
||||
|
||||
auto IsSame(const FsEntry& e) const {
|
||||
return root == e.root && type == e.type;
|
||||
}
|
||||
};
|
||||
|
||||
// roughly 1kib in size per entry
|
||||
struct FileEntry : FsDirectoryEntry {
|
||||
struct FileEntry final : FsDirectoryEntry {
|
||||
std::string extension{}; // if any
|
||||
std::string internal_name{}; // if any
|
||||
std::string internal_extension{}; // if any
|
||||
@@ -78,6 +126,7 @@ struct FileEntry : FsDirectoryEntry {
|
||||
bool checked_extension{}; // did we already search for an ext?
|
||||
bool checked_internal_extension{}; // did we already search for an ext?
|
||||
bool selected{}; // is this file selected?
|
||||
bool done_stat{}; // have we checked file_size / count.
|
||||
|
||||
auto IsFile() const -> bool {
|
||||
return type == FsDirEntryType_File;
|
||||
@@ -96,6 +145,11 @@ struct FileEntry : FsDirectoryEntry {
|
||||
}
|
||||
|
||||
auto GetExtension() const -> std::string {
|
||||
if (!checked_extension) {
|
||||
if (auto ext = std::strrchr(name, '.')) {
|
||||
return ext+1;
|
||||
}
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
@@ -154,13 +208,16 @@ struct FsDirCollection {
|
||||
|
||||
using FsDirCollections = std::vector<FsDirCollection>;
|
||||
|
||||
struct Menu;
|
||||
void SignalChange();
|
||||
|
||||
struct Base;
|
||||
|
||||
struct FsView final : Widget {
|
||||
friend class Menu;
|
||||
friend class Base;
|
||||
|
||||
FsView(Menu* menu, ViewSide side);
|
||||
FsView(Menu* menu, const fs::FsPath& path, const FsEntry& entry, ViewSide side);
|
||||
FsView(FsView* view, ViewSide side);
|
||||
FsView(Base* menu, ViewSide side);
|
||||
FsView(Base* menu, const std::shared_ptr<fs::Fs>& fs, const fs::FsPath& path, const FsEntry& entry, ViewSide side);
|
||||
~FsView();
|
||||
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
@@ -181,7 +238,13 @@ struct FsView final : Widget {
|
||||
|
||||
void SetSide(ViewSide side);
|
||||
|
||||
private:
|
||||
static Result DeleteAllCollections(ProgressBox* pbox, fs::Fs* fs, const FsDirCollections& collections, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles);
|
||||
static auto get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
|
||||
static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
|
||||
|
||||
// private:
|
||||
void OnClick();
|
||||
|
||||
void SetIndex(s64 index);
|
||||
void InstallForwarder();
|
||||
|
||||
@@ -190,7 +253,7 @@ private:
|
||||
void ZipFiles(fs::FsPath zip_path);
|
||||
void UploadFiles();
|
||||
|
||||
auto Scan(const fs::FsPath& new_path, bool is_walk_up = false) -> Result;
|
||||
auto Scan(fs::FsPath new_path, bool is_walk_up = false) -> Result;
|
||||
|
||||
auto GetNewPath(const FileEntry& entry) const -> fs::FsPath {
|
||||
return GetNewPath(m_path, entry.name);
|
||||
@@ -237,11 +300,11 @@ private:
|
||||
}
|
||||
|
||||
auto IsSd() const -> bool {
|
||||
return m_fs_entry.type == FsType::Sd;
|
||||
return m_fs_entry.IsSd();
|
||||
}
|
||||
|
||||
void Sort();
|
||||
void SortAndFindLastFile();
|
||||
void SortAndFindLastFile(bool scan = false);
|
||||
void SetIndexFromLastFile(const LastFile& last_file);
|
||||
|
||||
void OnDeleteCallback();
|
||||
@@ -249,13 +312,10 @@ private:
|
||||
void OnRenameCallback();
|
||||
auto CheckIfUpdateFolder() -> Result;
|
||||
|
||||
static auto get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
|
||||
static auto get_collections(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
|
||||
|
||||
auto get_collection(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollection& out, bool inc_file, bool inc_dir, bool inc_size) -> Result;
|
||||
auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out, bool inc_size = false) -> Result;
|
||||
|
||||
void SetFs(const fs::FsPath& new_path, const FsEntry& new_entry);
|
||||
void SetFs(const std::shared_ptr<fs::Fs>& fs, const fs::FsPath& new_path, const FsEntry& new_entry);
|
||||
|
||||
auto GetNative() -> fs::FsNative* {
|
||||
return (fs::FsNative*)m_fs.get();
|
||||
@@ -266,11 +326,17 @@ private:
|
||||
void DisplayOptions();
|
||||
void DisplayAdvancedOptions();
|
||||
|
||||
private:
|
||||
Menu* m_menu{};
|
||||
using MountFsFunc = Result(*)(fs::Fs *fs, const fs::FsPath &path, fs::FsPath &out_path);
|
||||
// using MountFsFunc = std::function<Result(fs::Fs *fs, const fs::FsPath &path, fs::FsPath &out_path)>;
|
||||
using UmountFsFunc = std::function<void(const fs::FsPath &mount)>;
|
||||
|
||||
void MountFileFs(const MountFsFunc& mount_func, const UmountFsFunc& umount_func);
|
||||
|
||||
// private:
|
||||
Base* m_menu{};
|
||||
ViewSide m_side{};
|
||||
|
||||
std::unique_ptr<fs::Fs> m_fs{};
|
||||
std::shared_ptr<fs::Fs> m_fs{};
|
||||
FsEntry m_fs_entry{};
|
||||
fs::FsPath m_path{};
|
||||
std::vector<FileEntry> m_entries{};
|
||||
@@ -296,7 +362,7 @@ private:
|
||||
|
||||
// contains all selected files for a command, such as copy, delete, cut etc.
|
||||
struct SelectedStash {
|
||||
void Add(std::shared_ptr<FsView> view, SelectedType type, const std::vector<FileEntry>& files, const fs::FsPath& path) {
|
||||
void Add(FsView* view, SelectedType type, const std::vector<FileEntry>& files, const fs::FsPath& path) {
|
||||
if (files.empty()) {
|
||||
Reset();
|
||||
} else {
|
||||
@@ -331,28 +397,34 @@ struct SelectedStash {
|
||||
}
|
||||
|
||||
// private:
|
||||
std::shared_ptr<FsView> m_view{};
|
||||
FsView* m_view{};
|
||||
std::vector<FileEntry> m_files{};
|
||||
fs::FsPath m_path{};
|
||||
SelectedType m_type{SelectedType::None};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
struct Base : MenuBase {
|
||||
friend class FsView;
|
||||
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
Base(u32 flags, u32 options);
|
||||
Base(const std::shared_ptr<fs::Fs>& fs, const FsEntry& fs_entry, const fs::FsPath& path, bool is_custom, u32 flags, u32 options);
|
||||
|
||||
void SetFilter(const std::vector<std::string>& filter) {
|
||||
m_filter = filter;
|
||||
}
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "Files"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
virtual void OnFocusGained() override;
|
||||
|
||||
static auto GetNewPath(const fs::FsPath& root_path, const fs::FsPath& file_path) -> fs::FsPath {
|
||||
return fs::AppendPath(root_path, file_path);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void OnClick(FsView* view, const FsEntry& fs_entry, const FileEntry& entry, const fs::FsPath& path);
|
||||
|
||||
protected:
|
||||
auto IsSplitScreen() const {
|
||||
return m_split_screen;
|
||||
}
|
||||
@@ -382,23 +454,40 @@ private:
|
||||
|
||||
void PromptIfShouldExit();
|
||||
|
||||
auto CanInstall() const {
|
||||
return m_options & FsOption_CanInstall;
|
||||
}
|
||||
|
||||
auto CreateFs(const FsEntry& fs_entry) -> std::shared_ptr<fs::Fs>;
|
||||
|
||||
private:
|
||||
void Init(const std::shared_ptr<fs::Fs>& fs, const FsEntry& fs_entry, const fs::FsPath& path, bool is_custom);
|
||||
|
||||
protected:
|
||||
static constexpr inline const char* INI_SECTION = "filebrowser";
|
||||
|
||||
std::shared_ptr<FsView> view{};
|
||||
std::shared_ptr<FsView> view_left{};
|
||||
std::shared_ptr<FsView> view_right{};
|
||||
const u32 m_options;
|
||||
|
||||
std::shared_ptr<fs::Fs> m_custom_fs{};
|
||||
FsEntry m_custom_fs_entry{};
|
||||
|
||||
FsView* view{};
|
||||
std::unique_ptr<FsView> view_left{};
|
||||
std::unique_ptr<FsView> view_right{};
|
||||
|
||||
std::vector<FileAssocEntry> m_assoc_entries{};
|
||||
SelectedStash m_selected{};
|
||||
|
||||
// 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{};
|
||||
s64 m_index{};
|
||||
s64 m_selected_count{};
|
||||
std::vector<std::string> m_filter{};
|
||||
|
||||
// local copy of nro entries that is loaded in LoadAssocEntriesPath()
|
||||
// if homebrew::GetNroEntries() returns nothing, usually due to
|
||||
// the menu not being loaded.
|
||||
// this is a bit of a hack to support replacing the homebrew menu tab,
|
||||
// sphaira wasn't really designed for this.
|
||||
// however this will work for now, until i add support for additional
|
||||
// nro scan mounts, at which point this won't scale.
|
||||
std::vector<NroEntry> m_nro_entries{};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Alphabetical};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
@@ -408,8 +497,39 @@ private:
|
||||
option::OptionBool m_ignore_read_only{INI_SECTION, "ignore_read_only", false};
|
||||
|
||||
bool m_loaded_assoc_entries{};
|
||||
bool m_is_update_folder{};
|
||||
bool m_split_screen{};
|
||||
};
|
||||
|
||||
struct Menu final : Base {
|
||||
Menu(u32 flags, u32 options = FsOption_All) : Base{flags, options} {
|
||||
}
|
||||
|
||||
Menu(const std::shared_ptr<fs::Fs>& fs, const FsEntry& fs_entry, const fs::FsPath& path, u32 options = FsOption_All)
|
||||
: Base{fs, fs_entry, path, true, MenuFlag_None, options} {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// case insensitive check
|
||||
auto IsSamePath(std::string_view a, std::string_view b) -> bool;
|
||||
auto IsExtension(std::string_view ext1, std::string_view ext2) -> bool;
|
||||
auto IsExtension(std::string_view ext, std::span<const std::string_view> list) -> bool;
|
||||
|
||||
struct FsStdioWrapper final : fs::FsStdio {
|
||||
using OnExit = std::function<void(void)>;
|
||||
FsStdioWrapper(const fs::FsPath& root, const OnExit& on_exit) : fs::FsStdio{true, root}, m_on_exit{on_exit} {
|
||||
|
||||
}
|
||||
|
||||
~FsStdioWrapper() {
|
||||
if (m_on_exit) {
|
||||
m_on_exit();
|
||||
}
|
||||
}
|
||||
|
||||
const OnExit m_on_exit;
|
||||
};
|
||||
|
||||
void MountFsHelper(const std::shared_ptr<fs::Fs>& fs, const fs::FsPath& name);
|
||||
|
||||
} // namespace sphaira::ui::menu::filebrowser
|
||||
|
||||
@@ -1,56 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
#include "yati/source/stream.hpp"
|
||||
#include "ui/menus/install_stream_menu_base.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::ftp {
|
||||
|
||||
enum class State {
|
||||
// not connected.
|
||||
None,
|
||||
// just connected, starts the transfer.
|
||||
Connected,
|
||||
// set whilst transfer is in progress.
|
||||
Progress,
|
||||
// set when the transfer is finished.
|
||||
Done,
|
||||
// failed to connect.
|
||||
Failed,
|
||||
};
|
||||
|
||||
struct StreamFtp final : yati::source::Stream {
|
||||
StreamFtp(const fs::FsPath& path, std::stop_token token);
|
||||
|
||||
Result ReadChunk(void* buf, s64 size, u64* bytes_read) override;
|
||||
bool Push(const void* buf, s64 size);
|
||||
void Disable();
|
||||
|
||||
// private:
|
||||
fs::FsPath m_path{};
|
||||
std::stop_token m_token{};
|
||||
std::vector<u8> m_buffer{};
|
||||
Mutex m_mutex{};
|
||||
bool m_active{};
|
||||
// bool m_push_exit{};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
struct Menu final : stream::Menu {
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "FTP"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
// this should be private
|
||||
// private:
|
||||
std::shared_ptr<StreamFtp> m_source{};
|
||||
Thread m_thread{};
|
||||
Mutex m_mutex{};
|
||||
// the below are shared across threads, lock with the above mutex!
|
||||
State m_state{State::None};
|
||||
void OnDisableInstallMode() override;
|
||||
|
||||
private:
|
||||
const char* m_user{};
|
||||
const char* m_pass{};
|
||||
unsigned m_port{};
|
||||
|
||||
@@ -2,34 +2,26 @@
|
||||
|
||||
#include "ui/menus/grid_menu_base.hpp"
|
||||
#include "ui/list.hpp"
|
||||
|
||||
#include "yati/container/base.hpp"
|
||||
#include "yati/nx/keys.hpp"
|
||||
|
||||
#include "title_info.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "option.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
namespace sphaira::ui::menu::game {
|
||||
|
||||
enum class NacpLoadStatus {
|
||||
// not yet attempted to be loaded.
|
||||
None,
|
||||
// started loading.
|
||||
Progress,
|
||||
// loaded, ready to parse.
|
||||
Loaded,
|
||||
// failed to load, do not attempt to load again!
|
||||
Error,
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
u64 app_id{};
|
||||
char display_version[0x10]{};
|
||||
u8 last_event{};
|
||||
NacpLanguageEntry lang{};
|
||||
int image{};
|
||||
bool selected{};
|
||||
|
||||
std::shared_ptr<NsApplicationControlData> control{};
|
||||
u64 control_size{};
|
||||
NacpLoadStatus status{NacpLoadStatus::None};
|
||||
title::NacpLoadStatus status{title::NacpLoadStatus::None};
|
||||
|
||||
auto GetName() const -> const char* {
|
||||
return lang.name;
|
||||
@@ -38,42 +30,6 @@ struct Entry {
|
||||
auto GetAuthor() const -> const char* {
|
||||
return lang.author;
|
||||
}
|
||||
|
||||
auto GetDisplayVersion() const -> const char* {
|
||||
return display_version;
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreadResultData {
|
||||
u64 id{};
|
||||
std::shared_ptr<NsApplicationControlData> control{};
|
||||
u64 control_size{};
|
||||
char display_version[0x10]{};
|
||||
NacpLanguageEntry lang{};
|
||||
NacpLoadStatus status{NacpLoadStatus::None};
|
||||
};
|
||||
|
||||
struct ThreadData {
|
||||
ThreadData();
|
||||
|
||||
auto IsRunning() const -> bool;
|
||||
void Run();
|
||||
void Close();
|
||||
void Push(u64 id);
|
||||
void Push(std::span<const Entry> entries);
|
||||
void Pop(std::vector<ThreadResultData>& out);
|
||||
|
||||
private:
|
||||
UEvent m_uevent{};
|
||||
Mutex m_mutex_id{};
|
||||
Mutex m_mutex_result{};
|
||||
|
||||
// app_ids pushed to the queue, signal uevent when pushed.
|
||||
std::vector<u64> m_ids{};
|
||||
// control data pushed to the queue.
|
||||
std::vector<ThreadResultData> m_result{};
|
||||
|
||||
std::atomic_bool m_running{};
|
||||
};
|
||||
|
||||
enum SortType {
|
||||
@@ -87,6 +43,8 @@ enum OrderType {
|
||||
|
||||
using LayoutType = grid::LayoutType;
|
||||
|
||||
void SignalChange();
|
||||
|
||||
struct Menu final : grid::Menu {
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
@@ -128,7 +86,9 @@ private:
|
||||
}
|
||||
|
||||
void DeleteGames();
|
||||
void DumpGames(u32 flags);
|
||||
void ExportOptions(bool to_nsz);
|
||||
void DumpGames(u32 flags, bool to_nsz);
|
||||
void CreateSaves(AccountUid uid);
|
||||
|
||||
private:
|
||||
static constexpr inline const char* INI_SECTION = "games";
|
||||
@@ -141,13 +101,79 @@ private:
|
||||
bool m_is_reversed{};
|
||||
bool m_dirty{};
|
||||
|
||||
ThreadData m_thread_data{};
|
||||
Thread m_thread{};
|
||||
// use for detection game card removal to force a refresh.
|
||||
Event m_gc_event{};
|
||||
FsEventNotifier m_gc_event_notifier{};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Updated};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_GridDetail};
|
||||
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_Grid};
|
||||
option::OptionBool m_hide_forwarders{INI_SECTION, "hide_forwarders", false};
|
||||
};
|
||||
|
||||
struct NcmMetaData {
|
||||
// points to global service, do not close manually!
|
||||
NcmContentStorage* cs{};
|
||||
NcmContentMetaDatabase* db{};
|
||||
u64 app_id{};
|
||||
NcmContentMetaKey key{};
|
||||
};
|
||||
|
||||
Result GetMetaEntries(const Entry& e, title::MetaEntries& out, u32 flags = title::ContentFlag_All);
|
||||
|
||||
Result GetNcmMetaFromMetaStatus(const NsApplicationContentMetaStatus& status, NcmMetaData& out);
|
||||
void DeleteMetaEntries(u64 app_id, int image, const std::string& name, const title::MetaEntries& entries);
|
||||
|
||||
struct TikEntry {
|
||||
FsRightsId id{};
|
||||
u8 key_gen{};
|
||||
std::vector<u8> tik_data{};
|
||||
std::vector<u8> cert_data{};
|
||||
};
|
||||
|
||||
struct NspEntry {
|
||||
// application name.
|
||||
std::string application_name{};
|
||||
// name of the nsp (name [id][v0][BASE].nsp).
|
||||
fs::FsPath path{};
|
||||
// tickets and cert data, will be empty if title key crypto isn't used.
|
||||
std::vector<TikEntry> tickets{};
|
||||
// all the collections for this nsp, such as nca's and tickets.
|
||||
std::vector<yati::container::CollectionEntry> collections{};
|
||||
// raw nsp data (header, file table and string table).
|
||||
std::vector<u8> nsp_data{};
|
||||
// size of the entier nsp.
|
||||
s64 nsp_size{};
|
||||
// copy of ncm cs, it is not closed.
|
||||
NcmContentStorage cs{};
|
||||
// copy of the icon, if invalid, it will use the default icon.
|
||||
int icon{};
|
||||
|
||||
Result Read(void* buf, s64 off, s64 size, u64* bytes_read);
|
||||
|
||||
private:
|
||||
static auto InRange(s64 off, s64 offset, s64 size) -> bool {
|
||||
return off < offset + size && off >= offset;
|
||||
}
|
||||
|
||||
static auto ClipSize(s64 off, s64 size, s64 file_size) -> s64 {
|
||||
return std::min(size, file_size - off);
|
||||
}
|
||||
};
|
||||
|
||||
struct ContentInfoEntry {
|
||||
NsApplicationContentMetaStatus status{};
|
||||
std::vector<NcmContentInfo> content_infos{};
|
||||
std::vector<NcmRightsId> ncm_rights_id{};
|
||||
};
|
||||
|
||||
auto BuildNspPath(const Entry& e, const NsApplicationContentMetaStatus& status, bool to_nsz = false) -> fs::FsPath;
|
||||
Result BuildContentEntry(const NsApplicationContentMetaStatus& status, ContentInfoEntry& out, bool to_nsz = false);
|
||||
Result BuildNspEntry(const Entry& e, const ContentInfoEntry& info, const keys::Keys& keys, NspEntry& out, bool to_nsz = false);
|
||||
Result BuildNspEntries(Entry& e, const title::MetaEntries& meta_entries, std::vector<NspEntry>& out, bool to_nsz = false);
|
||||
Result BuildNspEntries(Entry& e, u32 flags, std::vector<NspEntry>& out, bool to_nsz = false);
|
||||
|
||||
// dumps the array of nsp entries.
|
||||
void DumpNsp(const std::vector<NspEntry>& entries, bool to_nsz);
|
||||
|
||||
} // namespace sphaira::ui::menu::game
|
||||
|
||||
111
sphaira/include/ui/menus/game_meta_menu.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
#include "ui/menus/game_menu.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include "yati/nx/ncm.hpp"
|
||||
#include <span>
|
||||
#include <memory>
|
||||
|
||||
namespace sphaira::ui::menu::game::meta {
|
||||
|
||||
enum TicketType : u8 {
|
||||
TicketType_None,
|
||||
TicketType_Common,
|
||||
TicketType_Personalised,
|
||||
TicketType_Missing,
|
||||
};
|
||||
|
||||
struct MiniNacp {
|
||||
char display_version[0x10];
|
||||
};
|
||||
|
||||
struct MetaEntry {
|
||||
NsApplicationContentMetaStatus status{};
|
||||
ncm::ContentMeta content_meta{};
|
||||
// small version of nacp to speed up loading.
|
||||
MiniNacp nacp{};
|
||||
// total size of all ncas.
|
||||
s64 size{};
|
||||
// set to the key gen (if possible), only if title key encrypted.
|
||||
u8 key_gen{};
|
||||
// set to the ticket type.
|
||||
u8 ticket_type{TicketType_None};
|
||||
// set if it has missing ncas.
|
||||
u8 missing_count{};
|
||||
// set if selected.
|
||||
bool selected{};
|
||||
// set if we have checked the above meta data.
|
||||
bool checked{};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
Menu(Entry& entry);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "Meta"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void Scan();
|
||||
void UpdateSubheading();
|
||||
|
||||
auto GetSelectedEntries() const {
|
||||
title::MetaEntries out;
|
||||
for (auto& e : m_entries) {
|
||||
if (e.selected) {
|
||||
out.emplace_back(e.status);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_entries.empty() && out.empty()) {
|
||||
out.emplace_back(m_entries[m_index].status);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void ClearSelection() {
|
||||
for (auto& e : m_entries) {
|
||||
e.selected = false;
|
||||
}
|
||||
|
||||
m_selected_count = 0;
|
||||
}
|
||||
|
||||
auto GetEntry(u32 index) -> MetaEntry& {
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
auto GetEntry(u32 index) const -> const MetaEntry& {
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
auto GetEntry() -> MetaEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
auto GetEntry() const -> const MetaEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
void DumpGames(bool to_nsz);
|
||||
void DeleteGames();
|
||||
Result ResetRequiredSystemVersion(MetaEntry& entry) const;
|
||||
Result GetNcmSizeOfMetaStatus(MetaEntry& entry) const;
|
||||
|
||||
private:
|
||||
Entry& m_entry;
|
||||
std::vector<MetaEntry> m_entries{};
|
||||
s64 m_index{};
|
||||
s64 m_selected_count{};
|
||||
std::unique_ptr<List> m_list{};
|
||||
bool m_dirty{};
|
||||
|
||||
std::vector<FsRightsId> m_common_tickets{};
|
||||
std::vector<FsRightsId> m_personalised_tickets{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::game::meta
|
||||
92
sphaira/include/ui/menus/game_nca_menu.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
#include "ui/menus/game_meta_menu.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include "yati/nx/nca.hpp"
|
||||
#include "yati/nx/ncm.hpp"
|
||||
#include <span>
|
||||
#include <memory>
|
||||
|
||||
namespace sphaira::ui::menu::game::meta_nca {
|
||||
|
||||
struct NcaEntry {
|
||||
NcmContentId content_id{};
|
||||
u64 size{};
|
||||
u8 content_type{};
|
||||
// decrypted nca header.
|
||||
nca::Header header{};
|
||||
// set if missing.
|
||||
bool missing{};
|
||||
// set if selected.
|
||||
bool selected{};
|
||||
// set if we have checked the above meta data.
|
||||
bool checked{};
|
||||
};
|
||||
|
||||
struct Menu final : MenuBase {
|
||||
Menu(Entry& entry, const meta::MetaEntry& meta_entry);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "Nca"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void Scan();
|
||||
void UpdateSubheading();
|
||||
|
||||
auto GetSelectedEntries() const {
|
||||
std::vector<NcaEntry> out;
|
||||
for (auto& e : m_entries) {
|
||||
if (e.selected && !e.missing) {
|
||||
out.emplace_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_entries.empty() && out.empty()) {
|
||||
out.emplace_back(m_entries[m_index]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void ClearSelection() {
|
||||
for (auto& e : m_entries) {
|
||||
e.selected = false;
|
||||
}
|
||||
|
||||
m_selected_count = 0;
|
||||
}
|
||||
|
||||
auto GetEntry(u32 index) -> NcaEntry& {
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
auto GetEntry(u32 index) const -> const NcaEntry& {
|
||||
return m_entries[index];
|
||||
}
|
||||
|
||||
auto GetEntry() -> NcaEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
auto GetEntry() const -> const NcaEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
void DumpNcas();
|
||||
Result MountNcaFs();
|
||||
|
||||
private:
|
||||
Entry& m_entry;
|
||||
const meta::MetaEntry& m_meta_entry;
|
||||
NcmMetaData m_meta{};
|
||||
std::vector<NcaEntry> m_entries{};
|
||||
s64 m_index{};
|
||||
s64 m_selected_count{};
|
||||
std::unique_ptr<List> m_list{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::game::meta_nca
|
||||
@@ -7,7 +7,8 @@
|
||||
#include <span>
|
||||
#include <memory>
|
||||
|
||||
namespace sphaira::ui::menu::gc {
|
||||
// todo: pr to libnx
|
||||
extern "C" {
|
||||
|
||||
typedef enum {
|
||||
FsGameCardPartitionRaw_None = -1,
|
||||
@@ -15,6 +16,13 @@ typedef enum {
|
||||
FsGameCardPartitionRaw_Secure = 1,
|
||||
} FsGameCardPartitionRaw;
|
||||
|
||||
Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, FsGameCardPartitionRaw partition);
|
||||
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out);
|
||||
|
||||
}
|
||||
|
||||
namespace sphaira::ui::menu::gc {
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// The below structs are taken from nxdumptool./
|
||||
////////////////////////////////////////////////
|
||||
@@ -88,6 +96,33 @@ typedef struct {
|
||||
|
||||
static_assert(sizeof(GameCardInitialData) == 0x200);
|
||||
|
||||
/// Encrypted using AES-128-CTR with the key and IV/counter from the `GameCardTitleKeyAreaEncryption` section. Assumed to be all zeroes in retail gamecards.
|
||||
typedef struct {
|
||||
u8 titlekey[0x10]; ///< Decrypted titlekey from the `GameCardInitialData` section.
|
||||
u8 reserved[0xCF0];
|
||||
} GameCardTitleKeyArea;
|
||||
|
||||
static_assert(sizeof(GameCardTitleKeyArea) == 0xD00);
|
||||
|
||||
/// Encrypted using RSA-2048-OAEP and a private OAEP key from AuthoringTool. Assumed to be all zeroes in retail gamecards.
|
||||
typedef struct {
|
||||
u8 titlekey_encryption_key[0x10]; ///< Used as the AES-128-CTR key for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
|
||||
u8 titlekey_encryption_iv[0x10]; ///< Used as the AES-128-CTR IV/counter for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
|
||||
u8 reserved[0xE0];
|
||||
} GameCardTitleKeyAreaEncryption;
|
||||
|
||||
static_assert(sizeof(GameCardTitleKeyAreaEncryption) == 0x100);
|
||||
|
||||
/// Used to secure communications between the Lotus and the inserted gamecard.
|
||||
/// Supposedly precedes the gamecard header.
|
||||
typedef struct {
|
||||
GameCardInitialData initial_data;
|
||||
GameCardTitleKeyArea titlekey_area;
|
||||
GameCardTitleKeyAreaEncryption titlekey_area_encryption;
|
||||
} GameCardKeyArea;
|
||||
|
||||
static_assert(sizeof(GameCardKeyArea) == 0x1000);
|
||||
|
||||
typedef struct {
|
||||
u8 maker_code; ///< GameCardUidMakerCode.
|
||||
u8 version; ///< TODO: determine whether this matches GameCardVersion or not.
|
||||
@@ -151,8 +186,7 @@ struct ApplicationEntry {
|
||||
u64 app_id{};
|
||||
u32 version{};
|
||||
u8 key_gen{};
|
||||
std::unique_ptr<NsApplicationControlData> control{};
|
||||
u64 control_size{};
|
||||
std::vector<u8> icon;
|
||||
NacpLanguageEntry lang_entry{};
|
||||
|
||||
std::vector<GcCollections> application{};
|
||||
@@ -199,6 +233,9 @@ private:
|
||||
void FreeImage();
|
||||
void OnChangeIndex(s64 new_index);
|
||||
Result DumpGames(u32 flags);
|
||||
Result DumpXcz(u32 flags);
|
||||
|
||||
Result MountGcFs();
|
||||
|
||||
private:
|
||||
FsDeviceOperator m_dev_op{};
|
||||
@@ -221,12 +258,12 @@ private:
|
||||
|
||||
FsStorage m_storage{};
|
||||
// size of normal partition.
|
||||
s64 m_parition_normal_size{};
|
||||
s64 m_partition_normal_size{};
|
||||
// size of secure partition.
|
||||
s64 m_parition_secure_size{};
|
||||
s64 m_partition_secure_size{};
|
||||
// used size reported in the xci header.
|
||||
s64 m_storage_trimmed_size{};
|
||||
// total size of m_parition_normal_size + m_parition_secure_size.
|
||||
// total size of m_partition_normal_size + m_partition_secure_size.
|
||||
s64 m_storage_total_size{};
|
||||
// reported size via rom_size in the xci header.
|
||||
s64 m_storage_full_size{};
|
||||
|
||||
@@ -32,12 +32,15 @@ struct GhApiAsset {
|
||||
std::string content_type{};
|
||||
u64 size{};
|
||||
u64 download_count{};
|
||||
std::string updated_at{};
|
||||
std::string browser_download_url{};
|
||||
};
|
||||
|
||||
struct GhApiEntry {
|
||||
std::string tag_name{};
|
||||
std::string name{};
|
||||
std::string published_at{};
|
||||
bool prerelease{};
|
||||
std::vector<GhApiAsset> assets{};
|
||||
};
|
||||
|
||||
@@ -72,4 +75,18 @@ private:
|
||||
std::unique_ptr<List> m_list{};
|
||||
};
|
||||
|
||||
// creates a popup box on another thread.
|
||||
void DownloadEntries(const Entry& entry);
|
||||
|
||||
// parses the params into entry struct and calls DonwloadEntries
|
||||
bool Download(const std::string& url, const std::vector<AssetEntry>& assets = {}, const std::string& tag = {}, const std::string& pre_install_message = {}, const std::string& post_install_message = {});
|
||||
|
||||
// calls the above function by pushing the asset to an array.
|
||||
inline bool Download(const std::string& url, const AssetEntry& asset, const std::string& tag = {}, const std::string& pre_install_message = {}, const std::string& post_install_message = {}) {
|
||||
std::vector<AssetEntry> assets;
|
||||
assets.emplace_back(asset);
|
||||
|
||||
return Download(url, assets, tag, pre_install_message, post_install_message);
|
||||
}
|
||||
|
||||
} // namespace sphaira::ui::menu::gh
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
namespace sphaira::ui::menu::homebrew {
|
||||
|
||||
enum Filter {
|
||||
Filter_All,
|
||||
Filter_HideHidden,
|
||||
Filter_MAX,
|
||||
};
|
||||
|
||||
enum SortType {
|
||||
SortType_Updated,
|
||||
SortType_Alphabetical,
|
||||
@@ -25,9 +31,10 @@ enum OrderType {
|
||||
using LayoutType = grid::LayoutType;
|
||||
|
||||
auto GetNroEntries() -> std::span<const NroEntry>;
|
||||
void SignalChange();
|
||||
|
||||
struct Menu final : grid::Menu {
|
||||
Menu();
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "Apps"; };
|
||||
@@ -42,30 +49,45 @@ struct Menu final : grid::Menu {
|
||||
static Result InstallHomebrew(const fs::FsPath& path, const std::vector<u8>& icon);
|
||||
static Result InstallHomebrewFromPath(const fs::FsPath& path);
|
||||
|
||||
auto GetEntry(s64 i) -> NroEntry& {
|
||||
return m_entries[m_entries_current[i]];
|
||||
}
|
||||
|
||||
auto GetEntry() -> NroEntry& {
|
||||
return GetEntry(m_index);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void InstallHomebrew();
|
||||
void ScanHomebrew();
|
||||
void Sort();
|
||||
void SortAndFindLastFile();
|
||||
void SortAndFindLastFile(bool scan = false);
|
||||
void FreeEntries();
|
||||
void OnLayoutChange();
|
||||
void DisplayOptions();
|
||||
|
||||
auto IsStarEnabled() -> bool {
|
||||
return m_sort.Get() >= SortType_UpdatedStar;
|
||||
}
|
||||
|
||||
Result MountNroFs();
|
||||
|
||||
private:
|
||||
static constexpr inline const char* INI_SECTION = "homebrew";
|
||||
|
||||
std::vector<NroEntry> m_entries{};
|
||||
std::vector<u32> m_entries_index[Filter_MAX]{};
|
||||
std::span<u32> m_entries_current{};
|
||||
|
||||
s64 m_index{}; // where i am in the array
|
||||
std::unique_ptr<List> m_list{};
|
||||
bool m_dirty{};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_GridDetail};
|
||||
option::OptionBool m_hide_sphaira{INI_SECTION, "hide_sphaira", false};
|
||||
option::OptionBool m_show_hidden{INI_SECTION, "show_hidden", false};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::homebrew
|
||||
|
||||
36
sphaira/include/ui/menus/image_viewer.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/widget.hpp"
|
||||
#include "fs.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace sphaira::ui::menu::imageview {
|
||||
|
||||
struct Menu final : Widget {
|
||||
Menu(fs::Fs* fs, const fs::FsPath& path);
|
||||
~Menu();
|
||||
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
auto IsMenu() const -> bool override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateSize();
|
||||
|
||||
private:
|
||||
const fs::FsPath m_path;
|
||||
int m_image{};
|
||||
float m_image_width{};
|
||||
float m_image_height{};
|
||||
|
||||
// for zoom, 0.1 - 1.0
|
||||
float m_zoom{1};
|
||||
|
||||
// for pan.
|
||||
float m_xoff{};
|
||||
float m_yoff{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::imageview
|
||||
65
sphaira/include/ui/menus/install_stream_menu_base.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
#include "yati/source/stream.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::stream {
|
||||
|
||||
enum class State {
|
||||
// not connected.
|
||||
None,
|
||||
// just connected, starts the transfer.
|
||||
Connected,
|
||||
// set whilst transfer is in progress.
|
||||
Progress,
|
||||
// set when the transfer is finished.
|
||||
Done,
|
||||
// failed to connect.
|
||||
Failed,
|
||||
};
|
||||
|
||||
using OnInstallStart = std::function<bool(const char* path)>;
|
||||
using OnInstallWrite = std::function<bool(const void* buf, size_t size)>;
|
||||
using OnInstallClose = std::function<void()>;
|
||||
|
||||
struct Stream final : yati::source::Stream {
|
||||
Stream(const fs::FsPath& path, std::stop_token token);
|
||||
|
||||
Result ReadChunk(void* buf, s64 size, u64* bytes_read) override;
|
||||
bool Push(const void* buf, s64 size);
|
||||
void Disable();
|
||||
auto& GetPath() const { return m_path; }
|
||||
|
||||
private:
|
||||
fs::FsPath m_path{};
|
||||
std::stop_token m_token{};
|
||||
std::vector<u8> m_buffer{};
|
||||
CondVar m_can_read{};
|
||||
CondVar m_can_write{};
|
||||
|
||||
public:
|
||||
Mutex m_mutex{};
|
||||
std::atomic_bool m_active{};
|
||||
};
|
||||
|
||||
struct Menu : MenuBase {
|
||||
Menu(const std::string& title, u32 flags);
|
||||
virtual ~Menu();
|
||||
|
||||
virtual void Update(Controller* controller, TouchInfo* touch);
|
||||
virtual void Draw(NVGcontext* vg, Theme* theme);
|
||||
virtual void OnDisableInstallMode() = 0;
|
||||
|
||||
protected:
|
||||
bool OnInstallStart(const char* path);
|
||||
bool OnInstallWrite(const void* buf, size_t size);
|
||||
void OnInstallClose();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Stream> m_source{};
|
||||
Thread m_thread{};
|
||||
Mutex m_mutex{};
|
||||
State m_state{State::None};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::stream
|
||||
@@ -17,7 +17,7 @@ enum class UpdateState {
|
||||
Error,
|
||||
};
|
||||
|
||||
using MiscMenuFunction = std::function<std::shared_ptr<ui::menu::MenuBase>(u32 flags)>;
|
||||
using MiscMenuFunction = std::function<std::unique_ptr<MenuBase>(u32 flags)>;
|
||||
|
||||
enum MiscMenuFlag : u8 {
|
||||
// can be set as the rightside menu.
|
||||
@@ -31,6 +31,7 @@ struct MiscMenuEntry {
|
||||
const char* title;
|
||||
MiscMenuFunction func;
|
||||
u8 flag;
|
||||
const char* info;
|
||||
|
||||
auto IsShortcut() const -> bool {
|
||||
return flag & MiscMenuFlag_Shortcut;
|
||||
@@ -41,7 +42,7 @@ struct MiscMenuEntry {
|
||||
}
|
||||
};
|
||||
|
||||
auto GetMiscMenuEntries() -> std::span<const MiscMenuEntry>;
|
||||
auto GetMenuMenuEntries() -> std::span<const MiscMenuEntry>;
|
||||
|
||||
// this holds 2 menus and allows for switching between them
|
||||
struct MainMenu final : Widget {
|
||||
@@ -58,14 +59,14 @@ struct MainMenu final : Widget {
|
||||
}
|
||||
|
||||
private:
|
||||
void OnLRPress(std::shared_ptr<MenuBase> menu, Button b);
|
||||
void OnLRPress(MenuBase* menu, Button b);
|
||||
void AddOnLRPress();
|
||||
|
||||
private:
|
||||
std::shared_ptr<MenuBase> m_centre_menu{};
|
||||
std::shared_ptr<MenuBase> m_left_menu{};
|
||||
std::shared_ptr<MenuBase> m_right_menu{};
|
||||
std::shared_ptr<MenuBase> m_current_menu{};
|
||||
std::unique_ptr<MenuBase> m_centre_menu{};
|
||||
std::unique_ptr<MenuBase> m_left_menu{};
|
||||
std::unique_ptr<MenuBase> m_right_menu{};
|
||||
MenuBase* m_current_menu{};
|
||||
|
||||
std::string m_update_url{};
|
||||
std::string m_update_version{};
|
||||
|
||||
@@ -13,12 +13,14 @@ enum MenuFlag {
|
||||
|
||||
struct PolledData {
|
||||
struct tm tm{};
|
||||
u32 battery_percetange{};
|
||||
PsmChargerType charger_type{};
|
||||
NifmInternetConnectionType type{};
|
||||
NifmInternetConnectionStatus status{};
|
||||
u32 strength{};
|
||||
u32 ip{};
|
||||
s64 sd_free{1};
|
||||
s64 sd_total{1};
|
||||
s64 emmc_free{1};
|
||||
s64 emmc_total{1};
|
||||
};
|
||||
|
||||
struct MenuBase : Widget {
|
||||
@@ -33,9 +35,17 @@ struct MenuBase : Widget {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetTitle(std::string title);
|
||||
void SetTitleSubHeading(std::string sub_heading);
|
||||
void SetSubHeading(std::string sub_heading);
|
||||
void SetTitle(const std::string& title) {
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
void SetTitleSubHeading(const std::string& sub_heading) {
|
||||
m_title_sub_heading = sub_heading;
|
||||
}
|
||||
|
||||
void SetSubHeading(const std::string& sub_heading) {
|
||||
m_sub_heading = sub_heading;
|
||||
}
|
||||
|
||||
auto GetTitle() const {
|
||||
return m_title;
|
||||
|
||||
19
sphaira/include/ui/menus/mtp_menu.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/install_stream_menu_base.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::mtp {
|
||||
|
||||
struct Menu final : stream::Menu {
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "MTP"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void OnDisableInstallMode() override;
|
||||
|
||||
private:
|
||||
bool m_was_mtp_enabled{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::mtp
|
||||
124
sphaira/include/ui/menus/save_menu.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/grid_menu_base.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include "title_info.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "option.hpp"
|
||||
#include "dumper.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
|
||||
namespace sphaira::ui::menu::save {
|
||||
|
||||
enum BackupFlag {
|
||||
BackupFlag_None = 0,
|
||||
// option to allow the user to set the save file name.
|
||||
BackupFlag_SetName = 1 << 0,
|
||||
// set if this is a auto backup (on restore).
|
||||
BackupFlag_IsAuto = 1 << 1,
|
||||
};
|
||||
|
||||
struct Entry final : FsSaveDataInfo {
|
||||
NacpLanguageEntry lang{};
|
||||
int image{};
|
||||
bool selected{};
|
||||
title::NacpLoadStatus status{title::NacpLoadStatus::None};
|
||||
|
||||
auto GetName() const -> const char* {
|
||||
return lang.name;
|
||||
}
|
||||
|
||||
auto GetAuthor() const -> const char* {
|
||||
return lang.author;
|
||||
}
|
||||
};
|
||||
|
||||
enum SortType {
|
||||
SortType_Updated,
|
||||
};
|
||||
|
||||
enum OrderType {
|
||||
OrderType_Descending,
|
||||
OrderType_Ascending,
|
||||
};
|
||||
|
||||
using LayoutType = grid::LayoutType;
|
||||
|
||||
void SignalChange();
|
||||
|
||||
struct Menu final : grid::Menu {
|
||||
Menu(u32 flags);
|
||||
~Menu();
|
||||
|
||||
auto GetShortTitle() const -> const char* override { return "Saves"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void ScanHomebrew();
|
||||
void Sort();
|
||||
void SortAndFindLastFile(bool scan);
|
||||
void FreeEntries();
|
||||
void OnLayoutChange();
|
||||
|
||||
auto GetSelectedEntries() const {
|
||||
std::vector<Entry> out;
|
||||
for (auto& e : m_entries) {
|
||||
if (e.selected) {
|
||||
out.emplace_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_entries.empty() && out.empty()) {
|
||||
out.emplace_back(m_entries[m_index]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void ClearSelection() {
|
||||
for (auto& e : m_entries) {
|
||||
e.selected = false;
|
||||
}
|
||||
|
||||
m_selected_count = 0;
|
||||
}
|
||||
|
||||
void DisplayOptions();
|
||||
|
||||
void BackupSaves(std::vector<std::reference_wrapper<Entry>>& entries, u32 flags);
|
||||
void RestoreSave();
|
||||
|
||||
auto BuildSavePath(const Entry& e, u32 flags) const -> fs::FsPath;
|
||||
Result RestoreSaveInternal(ProgressBox* pbox, const Entry& e, const fs::FsPath& path);
|
||||
Result BackupSaveInternal(ProgressBox* pbox, const dump::DumpLocation& location, Entry& e, u32 flags);
|
||||
Result BackupSaveInternal(ProgressBox* pbox, const dump::DumpLocation& location, std::span<const std::reference_wrapper<Entry>> entries, u32 flags);
|
||||
|
||||
Result MountSaveFs();
|
||||
|
||||
private:
|
||||
static constexpr inline const char* INI_SECTION = "saves";
|
||||
|
||||
std::vector<Entry> m_entries{};
|
||||
s64 m_index{}; // where i am in the array
|
||||
s64 m_selected_count{};
|
||||
std::unique_ptr<List> m_list{};
|
||||
bool m_is_reversed{};
|
||||
bool m_dirty{};
|
||||
|
||||
std::vector<AccountProfileBase> m_accounts{};
|
||||
s64 m_account_index{};
|
||||
u8 m_data_type{FsSaveDataType_Account};
|
||||
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_Updated};
|
||||
option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending};
|
||||
option::OptionLong m_layout{INI_SECTION, "layout", LayoutType::LayoutType_Grid};
|
||||
option::OptionBool m_auto_backup_on_restore{INI_SECTION, "auto_backup_on_restore", true};
|
||||
option::OptionBool m_compress_save_backup{INI_SECTION, "compress_save_backup", true};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::save
|
||||
@@ -1,3 +1,4 @@
|
||||
#if 0
|
||||
#pragma once
|
||||
|
||||
#include "ui/menus/menu_base.hpp"
|
||||
@@ -104,7 +105,7 @@ struct Config {
|
||||
u32 limit{18};
|
||||
bool nsfw{false};
|
||||
|
||||
void SetQuery(std::string new_query) {
|
||||
void SetQuery(const std::string& new_query) {
|
||||
query = new_query;
|
||||
}
|
||||
|
||||
@@ -112,7 +113,7 @@ struct Config {
|
||||
query.clear();
|
||||
}
|
||||
|
||||
void SetCreator(Creator new_creator) {
|
||||
void SetCreator(const Creator& new_creator) {
|
||||
creator = new_creator.id;
|
||||
}
|
||||
|
||||
@@ -138,6 +139,7 @@ struct Menu final : MenuBase {
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index) {
|
||||
m_index = index;
|
||||
if (!m_index) {
|
||||
@@ -147,7 +149,7 @@ struct Menu final : MenuBase {
|
||||
|
||||
void InvalidateAllPages();
|
||||
void PackListDownload();
|
||||
void OnPackListDownload();
|
||||
void DisplayOptions();
|
||||
|
||||
private:
|
||||
static constexpr inline const char* INI_SECTION = "themezer";
|
||||
@@ -169,6 +171,9 @@ private:
|
||||
option::OptionLong m_sort{INI_SECTION, "sort", 0};
|
||||
option::OptionLong m_order{INI_SECTION, "order", 0};
|
||||
option::OptionBool m_nsfw{INI_SECTION, "nsfw", false};
|
||||
|
||||
bool m_checked_for_nro{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::themezer
|
||||
#endif
|
||||
|
||||
@@ -27,19 +27,16 @@ struct Menu final : MenuBase {
|
||||
auto GetShortTitle() const -> const char* override { return "USB"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
// this should be private
|
||||
// private:
|
||||
std::shared_ptr<yati::source::Usb> m_usb_source{};
|
||||
void ThreadFunction();
|
||||
|
||||
private:
|
||||
std::unique_ptr<yati::source::Usb> m_usb_source{};
|
||||
bool m_was_mtp_enabled{};
|
||||
|
||||
Thread m_thread{};
|
||||
Mutex m_mutex{};
|
||||
// the below are shared across threads, lock with the above mutex!
|
||||
State m_state{State::None};
|
||||
std::atomic<State> m_state{State::None};
|
||||
std::vector<std::string> m_names{};
|
||||
bool m_usb_has_connection{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::usb
|
||||
|
||||
45
sphaira/include/ui/music_player.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/widget.hpp"
|
||||
#include "ui/scrolling_text.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "utils/audio.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace sphaira::ui::music {
|
||||
|
||||
struct Menu final : Widget {
|
||||
Menu(fs::Fs* fs, const fs::FsPath& path);
|
||||
~Menu();
|
||||
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
private:
|
||||
void PauseToggle();
|
||||
void SeekForward();
|
||||
void SeekBack();
|
||||
|
||||
void IncreaseVolume();
|
||||
void DecreaseVolume();
|
||||
|
||||
private:
|
||||
audio::SongID m_song{};
|
||||
audio::Info m_info{};
|
||||
audio::Meta m_meta{};
|
||||
// only set if metadata was loaded.
|
||||
int m_icon{};
|
||||
|
||||
ScrollingText m_scroll_title{};
|
||||
ScrollingText m_scroll_artist{};
|
||||
ScrollingText m_scroll_album{};
|
||||
|
||||
// from movienx
|
||||
static constexpr Vec4 osd_progress_bar{400.f, 550, 1280.f - (400.f * 2.f), 10.f};
|
||||
// static constexpr Vec4 osd_progress_bar{300.f, SCREEN_HEIGHT / 2 - 15 / 2, 1280.f - (300.f * 2.f), 10.f};
|
||||
static constexpr Vec2 osd_time_text_left{osd_progress_bar.x - 12.f, osd_progress_bar.y - 2.f};
|
||||
static constexpr Vec2 osd_time_text_right{osd_progress_bar.x + osd_progress_bar.w + 12.f, osd_progress_bar.y - 2.f};
|
||||
static constexpr Vec4 osd_bar_outline{osd_time_text_left.x - 80, osd_progress_bar.y - 30, osd_progress_bar.w + 80 * 2 + 30, osd_progress_bar.h + 30 + 30};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::music
|
||||
@@ -11,8 +11,7 @@ public:
|
||||
enum class Side { LEFT, RIGHT };
|
||||
|
||||
public:
|
||||
NotifEntry(std::string text, Side side);
|
||||
~NotifEntry() = default;
|
||||
NotifEntry(const std::string& text, Side side);
|
||||
|
||||
auto Draw(NVGcontext* vg, Theme* theme, float y) -> bool;
|
||||
auto GetSide() const noexcept { return m_side; }
|
||||
@@ -22,17 +21,14 @@ private:
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
private:
|
||||
std::string m_text{};
|
||||
std::string m_text;
|
||||
Side m_side;
|
||||
std::size_t m_count{180}; // count down to zero
|
||||
Side m_side{};
|
||||
bool m_bounds_measured{};
|
||||
};
|
||||
|
||||
class NotifMananger final : public Object {
|
||||
public:
|
||||
NotifMananger() = default;
|
||||
~NotifMananger() = default;
|
||||
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
|
||||
void Push(const NotifEntry& entry);
|
||||
@@ -45,6 +41,7 @@ private:
|
||||
|
||||
private:
|
||||
void Draw(NVGcontext* vg, Theme* theme, Entries& entries);
|
||||
auto GetEntries(NotifEntry::Side side) -> Entries&;
|
||||
|
||||
private:
|
||||
Entries m_entries_left{};
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
namespace sphaira::ui::gfx {
|
||||
|
||||
void drawImage(NVGcontext*, float x, float y, float w, float h, int texture, float rounded = 0.F);
|
||||
void drawImage(NVGcontext*, const Vec4& v, int texture, float rounded = 0.F);
|
||||
void drawImage(NVGcontext*, float x, float y, float w, float h, int texture, float rounded = 0.F, float alpha = 1.0F);
|
||||
void drawImage(NVGcontext*, const Vec4& v, int texture, float rounded = 0.F, float alpha = 1.0F);
|
||||
|
||||
void dimBackground(NVGcontext*);
|
||||
|
||||
@@ -30,7 +30,8 @@ void drawTextArgs(NVGcontext*, float x, float y, float size, int align, const NV
|
||||
|
||||
void drawTextBox(NVGcontext*, float x, float y, float size, float bound, const NVGcolor& c, const char* str, int align = NVG_ALIGN_LEFT | NVG_ALIGN_TOP, const char* end = nullptr);
|
||||
|
||||
void textBounds(NVGcontext*, float x, float y, float *bounds, const char* str, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
void textBounds(NVGcontext*, float x, float y, float *bounds, const char* str);
|
||||
void textBoundsArgs(NVGcontext*, float x, float y, float *bounds, const char* str, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
|
||||
auto getButton(Button button) -> const char*;
|
||||
void drawScrollbar(NVGcontext*, const Theme*, u32 index_off, u32 count, u32 max_per_page);
|
||||
@@ -41,6 +42,8 @@ void drawScrollbar2(NVGcontext*, const Theme*, s64 index_off, s64 count, s64 row
|
||||
|
||||
void drawAppLable(NVGcontext* vg, const Theme*, ScrollingText& st, float x, float y, float w, const char* name);
|
||||
|
||||
void drawSpinner(NVGcontext* vg, const Theme*, float cx, float cy, float r, float t);
|
||||
|
||||
void updateHighlightAnimation();
|
||||
void getHighlightAnimation(float* gradientX, float* gradientY, float* color);
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
m_pos = { x, y, w, h };
|
||||
}
|
||||
|
||||
auto SetPos(Vec4 v) noexcept -> void {
|
||||
auto SetPos(const Vec4& v) noexcept -> void {
|
||||
m_pos = v;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class OptionBoxEntry final : public Widget {
|
||||
public:
|
||||
|
||||
public:
|
||||
OptionBoxEntry(const std::string& text, Vec4 pos);
|
||||
OptionBoxEntry(const std::string& text, const Vec4& pos);
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override {}
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
@@ -32,9 +32,10 @@ public:
|
||||
using Options = std::vector<Option>;
|
||||
|
||||
public:
|
||||
OptionBox(const std::string& message, const Option& a, Callback cb = [](auto){}, int image = 0); // confirm
|
||||
OptionBox(const std::string& message, const Option& a, const Option& b, Callback cb, int image = 0); // yesno
|
||||
OptionBox(const std::string& message, const Option& a, const Option& b, s64 index, Callback cb, int image = 0); // yesno
|
||||
OptionBox(const std::string& message, const Option& a, const Callback& cb = [](auto){}, int image = 0, bool own_image = false); // confirm
|
||||
OptionBox(const std::string& message, const Option& a, const Option& b, const Callback& cb, int image = 0, bool own_image = false); // yesno
|
||||
OptionBox(const std::string& message, const Option& a, const Option& b, s64 index, const Callback& cb, int image = 0, bool own_image = false); // yesno
|
||||
~OptionBox();
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
@@ -46,9 +47,10 @@ private:
|
||||
void SetIndex(s64 index);
|
||||
|
||||
private:
|
||||
std::string m_message{};
|
||||
Callback m_callback{};
|
||||
int m_image{};
|
||||
const std::string m_message;
|
||||
const Callback m_callback;
|
||||
const int m_image;
|
||||
const bool m_own_image;
|
||||
|
||||
Vec4 m_spacer_line{};
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ public:
|
||||
using Callback = std::function<void(std::optional<s64>)>;
|
||||
|
||||
public:
|
||||
explicit PopupList(std::string title, Items items, Callback cb, s64 index = 0);
|
||||
PopupList(std::string title, Items items, Callback cb, std::string index);
|
||||
PopupList(std::string title, Items items, std::string& index_str_ref, s64& index);
|
||||
PopupList(std::string title, Items items, std::string& index_ref);
|
||||
PopupList(std::string title, Items items, s64& index_ref);
|
||||
explicit PopupList(const std::string& title, const Items& items, const Callback& cb, s64 index = 0);
|
||||
PopupList(const std::string& title, const Items& items, const Callback& cb, const std::string& index);
|
||||
PopupList(const std::string& title, const Items& items, std::string& index_str_ref, s64& index);
|
||||
PopupList(const std::string& title, const Items& items, std::string& index_ref);
|
||||
PopupList(const std::string& title, const Items& items, s64& index_ref);
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
@@ -29,12 +29,12 @@ private:
|
||||
|
||||
private:
|
||||
static constexpr Vec2 m_title_pos{70.f, 28.f};
|
||||
static constexpr Vec4 m_block{280.f, 110.f, 720.f, 60.f};
|
||||
static constexpr Vec4 m_block{280.f, 110.f, SCREEN_HEIGHT, 60.f};
|
||||
static constexpr float m_text_xoffset{15.f};
|
||||
static constexpr float m_line_width{1220.f};
|
||||
|
||||
std::string m_title{};
|
||||
Items m_items{};
|
||||
const std::string m_title;
|
||||
const Items m_items;
|
||||
Callback m_callback{};
|
||||
s64 m_index{}; // index in list array
|
||||
s64 m_starting_index{};
|
||||
|
||||
@@ -11,22 +11,25 @@ namespace sphaira::ui {
|
||||
struct ProgressBox;
|
||||
using ProgressBoxCallback = std::function<Result(ProgressBox*)>;
|
||||
using ProgressBoxDoneCallback = std::function<void(Result rc)>;
|
||||
// using CancelCallback = std::function<void()>;
|
||||
|
||||
struct ProgressBox final : Widget {
|
||||
ProgressBox(
|
||||
int image,
|
||||
const std::string& action,
|
||||
const std::string& title,
|
||||
ProgressBoxCallback callback, ProgressBoxDoneCallback done = [](Result rc){},
|
||||
int cpuid = 1, int prio = PRIO_PREEMPTIVE, int stack_size = 1024*128
|
||||
const ProgressBoxCallback& callback, const ProgressBoxDoneCallback& done = nullptr
|
||||
);
|
||||
~ProgressBox();
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
|
||||
auto SetActionName(const std::string& action) -> ProgressBox&;
|
||||
auto SetTitle(const std::string& title) -> ProgressBox&;
|
||||
auto NewTransfer(const std::string& transfer) -> ProgressBox&;
|
||||
// zeros the saved offset.
|
||||
auto ResetTranfser() -> ProgressBox&;
|
||||
auto UpdateTransfer(s64 offset, s64 size) -> ProgressBox&;
|
||||
// not const in order to avoid copy by using std::swap
|
||||
auto SetImage(int image) -> ProgressBox&;
|
||||
@@ -37,15 +40,14 @@ struct ProgressBox final : Widget {
|
||||
auto ShouldExit() -> bool;
|
||||
auto ShouldExitResult() -> Result;
|
||||
|
||||
// helper functions
|
||||
auto CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src, const fs::FsPath& dst) -> Result;
|
||||
auto CopyFile(fs::Fs* fs, const fs::FsPath& src, const fs::FsPath& dst) -> Result;
|
||||
auto CopyFile(const fs::FsPath& src, const fs::FsPath& dst) -> Result;
|
||||
void Yield();
|
||||
void AddCancelEvent(UEvent* event);
|
||||
void RemoveCancelEvent(const UEvent* event);
|
||||
|
||||
auto GetCpuId() const {
|
||||
return m_cpuid;
|
||||
}
|
||||
// helper functions
|
||||
auto CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
|
||||
auto CopyFile(fs::Fs* fs, const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
|
||||
auto CopyFile(const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
|
||||
void Yield();
|
||||
|
||||
auto OnDownloadProgressCallback() {
|
||||
return [this](s64 dltotal, s64 dlnow, s64 ultotal, s64 ulnow){
|
||||
@@ -63,6 +65,11 @@ struct ProgressBox final : Widget {
|
||||
};
|
||||
}
|
||||
|
||||
// auto-clear = false
|
||||
auto GetCancelEvent() {
|
||||
return &m_uevent;
|
||||
}
|
||||
|
||||
private:
|
||||
void FreeImage();
|
||||
|
||||
@@ -74,10 +81,12 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
UEvent m_uevent{};
|
||||
Mutex m_mutex{};
|
||||
Thread m_thread{};
|
||||
ThreadData m_thread_data{};
|
||||
ProgressBoxDoneCallback m_done{};
|
||||
std::vector<UEvent*> m_cancel_events{};
|
||||
|
||||
// shared data start.
|
||||
std::string m_action{};
|
||||
@@ -96,7 +105,6 @@ private:
|
||||
ScrollingText m_scroll_title{};
|
||||
ScrollingText m_scroll_transfer{};
|
||||
|
||||
int m_cpuid{};
|
||||
int m_image{};
|
||||
bool m_own_image{};
|
||||
};
|
||||
|
||||
@@ -2,47 +2,136 @@
|
||||
|
||||
#include "ui/widget.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include "ui/scrolling_text.hpp"
|
||||
#include "option.hpp"
|
||||
#include <memory>
|
||||
#include <concepts>
|
||||
#include <utility>
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
namespace sphaira::ui {
|
||||
|
||||
class SidebarEntryBase : public Widget {
|
||||
public:
|
||||
SidebarEntryBase(std::string&& title);
|
||||
virtual auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
using DependsCallback = std::function<bool(void)>;
|
||||
using DependsClickCallback = std::function<void(void)>;
|
||||
|
||||
public:
|
||||
explicit SidebarEntryBase(const std::string& title, const std::string& info);
|
||||
|
||||
using Widget::Draw;
|
||||
virtual void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left);
|
||||
auto OnFocusGained() noexcept -> void override;
|
||||
auto OnFocusLost() noexcept -> void override;
|
||||
|
||||
void DrawEntry(NVGcontext* vg, Theme* theme, const std::string& left, const std::string& right, bool use_selected);
|
||||
|
||||
void Depends(const DependsCallback& callback, const std::string& depends_info, const DependsClickCallback& depends_click = {}) {
|
||||
m_depends_callback = callback;
|
||||
m_depends_info = depends_info;
|
||||
m_depends_click = depends_click;
|
||||
}
|
||||
|
||||
void Depends(bool& value, const std::string& depends_info, const DependsClickCallback& depends_click = {}) {
|
||||
m_depends_callback = [&value](){ return value; };
|
||||
m_depends_info = depends_info;
|
||||
m_depends_click = depends_click;
|
||||
}
|
||||
|
||||
void Depends(option::OptionBool& value, const std::string& depends_info, const DependsClickCallback& depends_click = {}) {
|
||||
m_depends_callback = [&value](){ return value.Get(); };
|
||||
m_depends_info = depends_info;
|
||||
m_depends_click = depends_click;
|
||||
}
|
||||
|
||||
void SetDirty(bool dirty = true) {
|
||||
m_dirty = dirty;
|
||||
}
|
||||
|
||||
auto IsDirty() const -> bool {
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string m_title;
|
||||
auto IsEnabled() const -> bool {
|
||||
if (m_depends_callback) {
|
||||
return m_depends_callback();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DependsClick() const {
|
||||
if (m_depends_click) {
|
||||
m_depends_click();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::string m_title;
|
||||
|
||||
private:
|
||||
const std::string m_info;
|
||||
std::string m_depends_info{};
|
||||
DependsCallback m_depends_callback{};
|
||||
DependsClickCallback m_depends_click{};
|
||||
ScrollingText m_scolling_title{};
|
||||
ScrollingText m_scolling_value{};
|
||||
bool m_dirty{};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept DerivedFromSidebarBase = std::is_base_of_v<SidebarEntryBase, T>;
|
||||
|
||||
class SidebarEntryBool final : public SidebarEntryBase {
|
||||
public:
|
||||
using Callback = std::function<void(bool&)>;
|
||||
|
||||
public:
|
||||
SidebarEntryBool(std::string title, bool option, Callback cb, std::string true_str = "On", std::string false_str = "Off");
|
||||
SidebarEntryBool(std::string title, bool& option, std::string true_str = "On", std::string false_str = "Off");
|
||||
explicit SidebarEntryBool(const std::string& title, bool option, const Callback& cb, const std::string& info = "", const std::string& true_str = "On", const std::string& false_str = "Off");
|
||||
explicit SidebarEntryBool(const std::string& title, bool& option, const std::string& info = "", const std::string& true_str = "On", const std::string& false_str = "Off");
|
||||
explicit SidebarEntryBool(const std::string& title, option::OptionBool& option, const Callback& cb, const std::string& info = "", const std::string& true_str = "On", const std::string& false_str = "Off");
|
||||
explicit SidebarEntryBool(const std::string& title, option::OptionBool& option, const std::string& info = "", const std::string& true_str = "On", const std::string& false_str = "Off");
|
||||
void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) override;
|
||||
|
||||
private:
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
|
||||
bool m_option;
|
||||
Callback m_callback;
|
||||
std::string m_true_str;
|
||||
std::string m_false_str;
|
||||
};
|
||||
|
||||
class SidebarEntrySlider final : public SidebarEntryBase {
|
||||
public:
|
||||
using Callback = std::function<void(float&)>;
|
||||
|
||||
public:
|
||||
explicit SidebarEntrySlider(const std::string& title, float value, float min, float max, int steps, const Callback& cb, const std::string& info = "");
|
||||
void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) override;
|
||||
|
||||
private:
|
||||
float m_value;
|
||||
float m_min;
|
||||
float m_max;
|
||||
int m_steps;
|
||||
Callback m_callback;
|
||||
|
||||
float m_duration;
|
||||
float m_inc;
|
||||
};
|
||||
|
||||
class SidebarEntryCallback final : public SidebarEntryBase {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
public:
|
||||
SidebarEntryCallback(std::string title, Callback cb, bool pop_on_click = false);
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
explicit SidebarEntryCallback(const std::string& title, const Callback& cb, const std::string& info);
|
||||
explicit SidebarEntryCallback(const std::string& title, const Callback& cb, bool pop_on_click = false, const std::string& info = "");
|
||||
void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) override;
|
||||
|
||||
private:
|
||||
Callback m_callback;
|
||||
bool m_pop_on_click;
|
||||
const Callback m_callback;
|
||||
const bool m_pop_on_click;
|
||||
};
|
||||
|
||||
class SidebarEntryArray final : public SidebarEntryBase {
|
||||
@@ -52,85 +141,144 @@ public:
|
||||
using Callback = std::function<void(s64& index)>;
|
||||
|
||||
public:
|
||||
explicit SidebarEntryArray(std::string title, Items items, Callback cb, s64 index = 0);
|
||||
SidebarEntryArray(std::string title, Items items, Callback cb, std::string index);
|
||||
SidebarEntryArray(std::string title, Items items, std::string& index);
|
||||
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
auto OnFocusGained() noexcept -> void override;
|
||||
auto OnFocusLost() noexcept -> void override;
|
||||
explicit SidebarEntryArray(const std::string& title, const Items& items, const Callback& cb, s64 index = 0, const std::string& info = "");
|
||||
explicit SidebarEntryArray(const std::string& title, const Items& items, const Callback& cb, const std::string& index, const std::string& info = "");
|
||||
explicit SidebarEntryArray(const std::string& title, const Items& items, std::string& index, const std::string& info = "");
|
||||
void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) override;
|
||||
|
||||
private:
|
||||
Items m_items;
|
||||
ListCallback m_list_callback;
|
||||
Callback m_callback;
|
||||
const Items m_items;
|
||||
const Callback m_callback;
|
||||
s64 m_index;
|
||||
s64 m_tick{};
|
||||
float m_text_yoff{};
|
||||
ListCallback m_list_callback{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SidebarEntrySlider final : public SidebarEntryBase {
|
||||
// single text entry.
|
||||
// the callback is called when the entry is clicked.
|
||||
// usually, the within the callback the text will be changed, use SetText().
|
||||
class SidebarEntryTextBase : public SidebarEntryBase {
|
||||
public:
|
||||
SidebarEntrySlider(std::string title, T& value, T min, T max)
|
||||
: SidebarEntryBase{title}
|
||||
, m_value{value}
|
||||
, m_min{min}
|
||||
, m_max{max} {
|
||||
using Callback = std::function<void(void)>;
|
||||
|
||||
public:
|
||||
explicit SidebarEntryTextBase(const std::string& title, const std::string& value, const Callback& cb, const std::string& info = "");
|
||||
|
||||
void Draw(NVGcontext* vg, Theme* theme, const Vec4& root_pos, bool left) override;
|
||||
|
||||
void SetCallback(const Callback& cb) {
|
||||
m_callback = cb;
|
||||
}
|
||||
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto GetValue() const -> const std::string& {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void SetValue(const std::string& value) {
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
private:
|
||||
T& m_value;
|
||||
T m_min;
|
||||
T m_max;
|
||||
T m_step{};
|
||||
Vec4 m_bar{};
|
||||
Vec4 m_bar_fill{};
|
||||
std::string m_value;
|
||||
Callback m_callback;
|
||||
};
|
||||
|
||||
class Sidebar final : public Widget {
|
||||
class SidebarEntryTextInput final : public SidebarEntryTextBase {
|
||||
public:
|
||||
enum class Side { LEFT, RIGHT };
|
||||
using Items = std::vector<std::shared_ptr<SidebarEntryBase>>;
|
||||
using Callback = std::function<void(SidebarEntryTextInput* input)>;
|
||||
|
||||
public:
|
||||
Sidebar(std::string title, Side side, Items&& items);
|
||||
Sidebar(std::string title, Side side);
|
||||
Sidebar(std::string title, std::string sub, Side side, Items&& items);
|
||||
Sidebar(std::string title, std::string sub, Side side);
|
||||
// uses normal keyboard.
|
||||
explicit SidebarEntryTextInput(const std::string& title, const std::string& value, const std::string& header = {}, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "", const Callback& callback = nullptr);
|
||||
// uses numpad.
|
||||
explicit SidebarEntryTextInput(const std::string& title, s64 value, const std::string& header = {}, const std::string& guide = {}, s64 len_min = -1, s64 len_max = PATH_MAX, const std::string& info = "", const Callback& callback = nullptr);
|
||||
|
||||
auto GetNumValue() const -> s64 {
|
||||
return std::stoul(GetValue());
|
||||
}
|
||||
|
||||
void SetNumValue(s64 value) {
|
||||
SetValue(std::to_string(value));
|
||||
}
|
||||
private:
|
||||
const std::string m_header;
|
||||
const std::string m_guide;
|
||||
const s64 m_len_min;
|
||||
const s64 m_len_max;
|
||||
const Callback m_callback;
|
||||
};
|
||||
|
||||
class SidebarEntryFilePicker final : public SidebarEntryTextBase {
|
||||
public:
|
||||
explicit SidebarEntryFilePicker(const std::string& title, const std::string& value, const std::vector<std::string>& filter, const std::string& info = "");
|
||||
|
||||
// extension filter.
|
||||
void SetFilter(const std::vector<std::string>& filter) {
|
||||
m_filter = filter;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_filter{};
|
||||
};
|
||||
|
||||
class Sidebar : public Widget {
|
||||
public:
|
||||
enum class Side { LEFT, RIGHT };
|
||||
using Items = std::vector<std::unique_ptr<SidebarEntryBase>>;
|
||||
using OnExitWhenChangedCallback = std::function<void()>;
|
||||
|
||||
public:
|
||||
explicit Sidebar(const std::string& title, Side side, float width = 450.f);
|
||||
explicit Sidebar(const std::string& title, const std::string& sub, Side side, float width = 450.f);
|
||||
~Sidebar();
|
||||
|
||||
auto Update(Controller* controller, TouchInfo* touch) -> void override;
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
auto OnFocusGained() noexcept -> void override;
|
||||
auto OnFocusLost() noexcept -> void override;
|
||||
|
||||
void Add(std::shared_ptr<SidebarEntryBase> entry);
|
||||
auto Add(std::unique_ptr<SidebarEntryBase>&& entry) -> SidebarEntryBase*;
|
||||
|
||||
template<DerivedFromSidebarBase T, typename... Args>
|
||||
auto Add(Args&&... args) -> T* {
|
||||
return (T*)Add(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
// sets a callback that is called on exit when the any options were changed.
|
||||
// the change detection isn't perfect, it just checks if the A button was pressed...
|
||||
void SetOnExitWhenChanged(const OnExitWhenChangedCallback& cb) {
|
||||
m_on_exit_when_changed = cb;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetIndex(s64 index);
|
||||
void SetupButtons();
|
||||
|
||||
private:
|
||||
std::string m_title;
|
||||
std::string m_sub;
|
||||
Side m_side;
|
||||
Items m_items;
|
||||
const std::string m_title;
|
||||
const std::string m_sub;
|
||||
const Side m_side;
|
||||
Items m_items{};
|
||||
s64 m_index{};
|
||||
|
||||
std::unique_ptr<List> m_list;
|
||||
std::unique_ptr<List> m_list{};
|
||||
|
||||
Vec4 m_top_bar{};
|
||||
Vec4 m_bottom_bar{};
|
||||
Vec2 m_title_pos{};
|
||||
Vec4 m_base_pos{};
|
||||
|
||||
OnExitWhenChangedCallback m_on_exit_when_changed{};
|
||||
|
||||
static constexpr float m_title_size{28.f};
|
||||
// static constexpr Vec2 box_size{380.f, 70.f};
|
||||
static constexpr Vec2 m_box_size{400.f, 70.f};
|
||||
};
|
||||
|
||||
class FormSidebar : public Sidebar {
|
||||
public:
|
||||
explicit FormSidebar(const std::string& title) : Sidebar{title, Side::LEFT, 540.f} {
|
||||
// explicit FormSidebar(const std::string& title) : Sidebar{title, Side::LEFT} {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "nanovg.h"
|
||||
#include "pulsar.h"
|
||||
#include "fs.hpp"
|
||||
|
||||
#include <switch.h>
|
||||
@@ -56,8 +55,8 @@ struct Vec2 {
|
||||
struct Vec4 {
|
||||
constexpr Vec4() = default;
|
||||
constexpr Vec4(float _x, float _y, float _w, float _h) : x{_x}, y{_y}, w{_w}, h{_h} {}
|
||||
constexpr Vec4(Vec2 vec0, Vec2 vec1) : x{vec0.x}, y{vec0.y}, w{vec1.x}, h{vec1.y} {}
|
||||
constexpr Vec4(Vec4 vec0, Vec4 vec1) : x{vec0.x}, y{vec0.y}, w{vec1.w}, h{vec1.h} {}
|
||||
constexpr Vec4(const Vec2& vec0, const Vec2& vec1) : x{vec0.x}, y{vec0.y}, w{vec1.x}, h{vec1.y} {}
|
||||
constexpr Vec4(const Vec4& vec0, const Vec4& vec1) : x{vec0.x}, y{vec0.y}, w{vec1.w}, h{vec1.h} {}
|
||||
|
||||
float& operator[](std::size_t idx) {
|
||||
switch (idx) {
|
||||
@@ -179,6 +178,8 @@ enum ThemeEntryID {
|
||||
ThemeEntryID_TEXT_SELECTED,
|
||||
// background colour of a selected item, can be an image (not recommended).
|
||||
ThemeEntryID_SELECTED_BACKGROUND,
|
||||
// colour of the split screen and selected item.
|
||||
ThemeEntryID_FOCUS,
|
||||
|
||||
// colour of line separators in a list.
|
||||
ThemeEntryID_LINE,
|
||||
@@ -227,6 +228,7 @@ struct ThemeMeta {
|
||||
struct Theme {
|
||||
ThemeMeta meta;
|
||||
ElementEntry elements[ThemeEntryID_MAX];
|
||||
fs::FsPath music_path;
|
||||
|
||||
auto GetColour(ThemeEntryID id) const {
|
||||
return elements[id].colour;
|
||||
@@ -272,6 +274,13 @@ enum class Button : u64 {
|
||||
START = static_cast<u64>(HidNpadButton_Plus),
|
||||
SELECT = static_cast<u64>(HidNpadButton_Minus),
|
||||
|
||||
SL_LEFT = static_cast<u64>(HidNpadButton_LeftSL),
|
||||
SR_LEFT = static_cast<u64>(HidNpadButton_LeftSR),
|
||||
SL_RIGHT = static_cast<u64>(HidNpadButton_RightSL),
|
||||
SR_RIGHT = static_cast<u64>(HidNpadButton_RightSR),
|
||||
SL_ANY = SL_LEFT | SL_RIGHT,
|
||||
SR_ANY = SR_LEFT | SR_RIGHT,
|
||||
|
||||
// todo:
|
||||
DPAD_LEFT = static_cast<u64>(HidNpadButton_Left),
|
||||
DPAD_RIGHT = static_cast<u64>(HidNpadButton_Right),
|
||||
@@ -282,11 +291,13 @@ enum class Button : u64 {
|
||||
LS_RIGHT = static_cast<u64>(HidNpadButton_StickLRight),
|
||||
LS_UP = static_cast<u64>(HidNpadButton_StickLUp),
|
||||
LS_DOWN = static_cast<u64>(HidNpadButton_StickLDown),
|
||||
LS_ANY = LS_LEFT | LS_RIGHT | LS_UP | LS_DOWN,
|
||||
|
||||
RS_LEFT = static_cast<u64>(HidNpadButton_StickRLeft),
|
||||
RS_RIGHT = static_cast<u64>(HidNpadButton_StickRRight),
|
||||
RS_UP = static_cast<u64>(HidNpadButton_StickRUp),
|
||||
RS_DOWN = static_cast<u64>(HidNpadButton_StickRDown),
|
||||
RS_ANY = RS_LEFT | RS_RIGHT | RS_UP | RS_DOWN,
|
||||
|
||||
ANY_LEFT = static_cast<u64>(HidNpadButton_AnyLeft),
|
||||
ANY_RIGHT = static_cast<u64>(HidNpadButton_AnyRight),
|
||||
@@ -330,10 +341,10 @@ struct Action final {
|
||||
CallbackWithBool
|
||||
>;
|
||||
|
||||
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} {}
|
||||
explicit Action(const Callback& cb) : Action{ActionType::DOWN, "", cb} {}
|
||||
explicit Action(const std::string& hint, const Callback& cb) : Action{ActionType::DOWN, hint, cb} {}
|
||||
explicit Action(u8 type, const Callback& cb) : Action{type, "", cb} {}
|
||||
explicit Action(u8 type, const std::string& hint, const Callback& cb) : m_type{type}, m_callback{cb}, m_hint{hint} {}
|
||||
|
||||
auto IsHidden() const noexcept { return m_hint.empty(); }
|
||||
|
||||
@@ -355,6 +366,81 @@ struct Action final {
|
||||
std::string m_hint{};
|
||||
};
|
||||
|
||||
struct GenericHidState {
|
||||
GenericHidState() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
buttons_cur = 0;
|
||||
buttons_old = 0;
|
||||
}
|
||||
|
||||
u64 GetButtons() const {
|
||||
return buttons_cur;
|
||||
}
|
||||
|
||||
u64 GetButtonsDown() const {
|
||||
return buttons_cur & ~buttons_old;
|
||||
}
|
||||
|
||||
u64 GetButtonsUp() const {
|
||||
return ~buttons_cur & buttons_old;
|
||||
}
|
||||
|
||||
virtual void Update() = 0;
|
||||
|
||||
protected:
|
||||
u64 buttons_cur;
|
||||
u64 buttons_old;
|
||||
};
|
||||
|
||||
struct KeyboardState final : GenericHidState {
|
||||
struct MapEntry {
|
||||
HidKeyboardKey key;
|
||||
u64 button;
|
||||
};
|
||||
using Map = std::span<const MapEntry>;
|
||||
|
||||
void Init(Map map) {
|
||||
m_map = map;
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Update() override {
|
||||
buttons_old = buttons_cur;
|
||||
buttons_cur = 0;
|
||||
|
||||
if (!hidGetKeyboardStates(&m_state, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ctrl = m_state.modifiers & HidKeyboardModifier_Control;
|
||||
const auto shift = m_state.modifiers & HidKeyboardModifier_Shift;
|
||||
|
||||
for (const auto& map : m_map) {
|
||||
if (hidKeyboardStateGetKey(&m_state, map.key)) {
|
||||
if (shift && map.button == static_cast<u64>(Button::L)) {
|
||||
buttons_cur |= static_cast<u64>(Button::L2);
|
||||
} else if (shift && map.button == static_cast<u64>(Button::R)) {
|
||||
buttons_cur |= static_cast<u64>(Button::R2);
|
||||
} else if (ctrl && map.button == static_cast<u64>(Button::L)) {
|
||||
buttons_cur |= static_cast<u64>(Button::L3);
|
||||
} else if (ctrl && map.button == static_cast<u64>(Button::R)) {
|
||||
buttons_cur |= static_cast<u64>(Button::R3);
|
||||
} else {
|
||||
buttons_cur |= map.button;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Map m_map{};
|
||||
HidKeyboardState m_state{};
|
||||
};
|
||||
|
||||
struct Controller {
|
||||
u64 m_kdown{};
|
||||
u64 m_kheld{};
|
||||
@@ -386,26 +472,44 @@ struct Controller {
|
||||
m_kup = 0;
|
||||
}
|
||||
|
||||
void UpdateButtonHeld(u64 buttons) {
|
||||
void UpdateButtonHeld(u64 buttons, double delta) {
|
||||
if (m_kdown & buttons) {
|
||||
m_step = 50;
|
||||
m_step_max = m_MAX_STEP;
|
||||
m_step = m_INC_STEP;
|
||||
m_counter = 0;
|
||||
m_step_max_counter = 0;
|
||||
} else if (m_kheld & buttons) {
|
||||
m_counter += m_step;
|
||||
m_counter += m_step * delta;
|
||||
|
||||
// if we are at the max, ignore the delta and go as fast as the frame rate.
|
||||
if (m_step_max == m_MAX) {
|
||||
m_counter = m_MAX;
|
||||
}
|
||||
|
||||
if (m_counter >= m_MAX) {
|
||||
m_kdown |= m_kheld & buttons;
|
||||
m_counter = 0;
|
||||
m_step = std::min(m_step + 50, m_MAX_STEP);
|
||||
m_step = std::min(m_step + m_INC_STEP, m_step_max);
|
||||
|
||||
// slowly speed up until we reach 1 button down per frame.
|
||||
m_step_max_counter++;
|
||||
if (m_step_max_counter >= 5) {
|
||||
m_step_max_counter = 0;
|
||||
m_step_max = std::min(m_step_max + m_INC_STEP, m_MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int m_MAX = 1000;
|
||||
static constexpr int m_MAX_STEP = 250;
|
||||
int m_step = 50;
|
||||
int m_counter = 0;
|
||||
static constexpr double m_MAX = 1000;
|
||||
static constexpr double m_MAX_STEP = 250;
|
||||
static constexpr double m_INC_STEP = 50;
|
||||
|
||||
double m_step_max = m_MAX_STEP;
|
||||
double m_step = m_INC_STEP;
|
||||
double m_counter = 0;
|
||||
int m_step_max_counter = 0;
|
||||
};
|
||||
|
||||
} // namespace sphaira
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <concepts>
|
||||
|
||||
namespace sphaira::ui {
|
||||
|
||||
struct uiButton final : Object {
|
||||
uiButton(Button button, Action action) : m_button{button}, m_action{action} {}
|
||||
uiButton(Button button, const std::string& button_str, const std::string& action_str);
|
||||
uiButton(Button button, const std::string& action_str);
|
||||
|
||||
auto Draw(NVGcontext* vg, Theme* theme) -> void override;
|
||||
|
||||
Button m_button;
|
||||
Action m_action;
|
||||
std::string m_button_str;
|
||||
std::string m_action_str;
|
||||
Vec4 m_button_pos{};
|
||||
Vec4 m_hint_pos{};
|
||||
};
|
||||
@@ -44,8 +48,8 @@ struct Widget : public Object {
|
||||
}
|
||||
|
||||
auto HasAction(Button button) const -> bool;
|
||||
void SetAction(Button button, Action action);
|
||||
void SetActions(std::same_as<std::pair<Button, Action>> auto ...args) {
|
||||
void SetAction(Button button, const Action& action);
|
||||
void SetActions(std::same_as<std::pair<Button, Action>> auto&& ...args) {
|
||||
const std::array list = {args...};
|
||||
for (const auto& [button, action] : list) {
|
||||
SetAction(button, action);
|
||||
@@ -62,6 +66,12 @@ struct Widget : public Object {
|
||||
m_actions.clear();
|
||||
}
|
||||
|
||||
void RemoveActions(const Actions& actions) {
|
||||
for (auto& e : actions) {
|
||||
RemoveAction(e.first);
|
||||
}
|
||||
}
|
||||
|
||||
auto FireAction(Button button, u8 type = ActionType::DOWN) -> bool;
|
||||
|
||||
void SetPop(bool pop = true) {
|
||||
@@ -77,6 +87,8 @@ struct Widget : public Object {
|
||||
}
|
||||
|
||||
auto GetUiButtons() const -> uiButtons;
|
||||
static void SetupUiButtons(uiButtons& buttons, const Vec2& button_pos = {1220, 675});
|
||||
static auto GetUiButtons(const Actions& actions, const Vec2& button_pos = {1220, 675}) -> uiButtons;
|
||||
|
||||
Actions m_actions{};
|
||||
Vec2 m_button_pos{1220, 675};
|
||||
@@ -84,4 +96,7 @@ struct Widget : public Object {
|
||||
bool m_pop{false};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept DerivedFromWidget = std::is_base_of_v<Widget, T>;
|
||||
|
||||
} // namespace sphaira::ui
|
||||
|
||||
@@ -2,18 +2,12 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <new>
|
||||
#include <memory>
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::usb {
|
||||
|
||||
struct Base {
|
||||
enum { USBModule = 523 };
|
||||
|
||||
enum : Result {
|
||||
Result_Cancelled = MAKERESULT(USBModule, 100),
|
||||
};
|
||||
|
||||
Base(u64 transfer_timeout);
|
||||
virtual ~Base();
|
||||
|
||||
@@ -45,43 +39,10 @@ struct Base {
|
||||
ueventSignal(GetCancelEvent());
|
||||
}
|
||||
|
||||
auto& GetTransferBuffer() {
|
||||
return m_aligned;
|
||||
}
|
||||
|
||||
auto GetTransferTimeout() const {
|
||||
return m_transfer_timeout;
|
||||
}
|
||||
|
||||
public:
|
||||
// custom allocator for std::vector that respects alignment.
|
||||
// https://en.cppreference.com/w/cpp/named_req/Allocator
|
||||
template <typename T, std::size_t Align>
|
||||
struct CustomVectorAllocator {
|
||||
public:
|
||||
// https://en.cppreference.com/w/cpp/memory/new/operator_new
|
||||
auto allocate(std::size_t n) -> T* {
|
||||
n = (n + (Align - 1)) &~ (Align - 1);
|
||||
return new(align) T[n];
|
||||
}
|
||||
|
||||
// https://en.cppreference.com/w/cpp/memory/new/operator_delete
|
||||
auto deallocate(T* p, std::size_t n) noexcept -> void {
|
||||
// ::operator delete[] (p, n, align);
|
||||
::operator delete[] (p, align);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr inline std::align_val_t align{Align};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PageAllocator : CustomVectorAllocator<T, 0x1000> {
|
||||
using value_type = T; // used by std::vector
|
||||
};
|
||||
|
||||
using PageAlignedVector = std::vector<u8, PageAllocator<u8>>;
|
||||
|
||||
protected:
|
||||
enum UsbSessionEndpoint {
|
||||
UsbSessionEndpoint_In = 0,
|
||||
@@ -96,7 +57,7 @@ protected:
|
||||
private:
|
||||
u64 m_transfer_timeout{};
|
||||
UEvent m_uevent{};
|
||||
PageAlignedVector m_aligned{};
|
||||
std::unique_ptr<u8*> m_aligned{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::usb
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::usb::tinfoil {
|
||||
|
||||
enum Magic : u32 {
|
||||
Magic_List0 = 0x304C5554, // TUL0 (Tinfoil Usb List 0)
|
||||
Magic_Command0 = 0x30435554, // TUC0 (Tinfoil USB Command 0)
|
||||
};
|
||||
|
||||
enum USBCmdType : u8 {
|
||||
REQUEST = 0,
|
||||
RESPONSE = 1
|
||||
};
|
||||
|
||||
enum USBCmdId : u32 {
|
||||
EXIT = 0,
|
||||
FILE_RANGE = 1
|
||||
};
|
||||
|
||||
// extension flags for sphaira.
|
||||
enum USBFlag : u8 {
|
||||
USBFlag_NONE = 0,
|
||||
// stream install, does not allow for random access.
|
||||
// allows the upload to be multi threaded., do not modify!
|
||||
// the order of the file list must be kept as-is.
|
||||
USBFlag_STREAM = 1 << 0,
|
||||
};
|
||||
|
||||
struct TUSHeader {
|
||||
u32 magic; // TUL0 (Tinfoil Usb List 0)
|
||||
u32 nspListSize;
|
||||
u8 flags;
|
||||
u8 padding[0x7];
|
||||
};
|
||||
|
||||
struct NX_PACKED USBCmdHeader {
|
||||
u32 magic; // TUC0 (Tinfoil USB Command 0)
|
||||
USBCmdType type;
|
||||
u8 padding[0x3];
|
||||
u32 cmdId;
|
||||
u64 dataSize;
|
||||
u8 reserved[0xC];
|
||||
};
|
||||
|
||||
struct FileRangeCmdHeader {
|
||||
u64 size;
|
||||
u64 offset;
|
||||
u64 nspNameLen;
|
||||
u64 padding;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TUSHeader) == 0x10, "TUSHeader must be 0x10!");
|
||||
static_assert(sizeof(USBCmdHeader) == 0x20, "USBCmdHeader must be 0x20!");
|
||||
|
||||
} // namespace sphaira::usb::tinfoil
|
||||