Compare commits

..

289 Commits

Author SHA1 Message Date
J-D-K
c094030c52 Minor code revisions in preparation for cache system on 20.0+ 2025-05-28 18:33:58 -04:00
JK
291d2fd82f
Merge pull request #250 from impeeza/impeeza-ES-419-Update
ES-419 update
2024-11-05 11:36:59 -05:00
impeeza
df03aac552
ES-419 update
Seems the update of this file get lost in between commits.
2024-11-05 11:22:58 -05:00
J-D-K
24aafcbf63 Update build date 2024-11-05 10:10:20 -05:00
J-D-K
1e06516317 Settings Readme: Fix more typos I missed. 2024-10-31 21:14:59 -04:00
J-D-K
da121677fb Update Settings Readme and fix a few typos 2024-10-31 17:08:25 -04:00
J-D-K
361b862bf3 Update settings readme with note. 2024-10-31 14:29:29 -04:00
J-D-K
790e4bebdf Add settings readme 2024-10-31 14:25:52 -04:00
J-D-K
9ac5f39476 Tweak: Make config save in a few more places. 2024-10-31 13:42:44 -04:00
J-D-K
44a466fef0 Update Makefile build date 2024-10-31 11:04:34 -04:00
J-D-K
04ce90c7dd Change default config settings, make config save on toggle. 2024-10-31 10:35:21 -04:00
JK
ddbf601819
Merge pull request #212 from karasuhebi/master-1
Fixing typo
2024-10-16 15:46:10 -04:00
JK
9b8c852b5c
Merge pull request #244 from DDinghoya/patch-2
Update ko.txt
2024-10-16 13:07:57 -04:00
JK
ddcebdcff6
Merge pull request #240 from impeeza/patch-1
Update REMOTE_INSTRUCTIONS.MD
2024-10-16 13:07:27 -04:00
JK
cf39346d69
Merge pull request #191 from Pf-16/patch-3
Update it.txt
2024-10-16 13:07:11 -04:00
JK
7949582fd8
Merge pull request #182 from qazrfv1234/qazrfv1234-patch-4
Update zh-TW.txt
2024-10-16 13:06:55 -04:00
DDinghoya
1bda18d98f
Update ko.txt 2024-09-22 00:32:07 +09:00
impeeza
3f39769b52
Update REMOTE_INSTRUCTIONS.MD
Small clarification about the use of `basepath`, thanks to @nimaskji on https://github.com/J-D-K/JKSV/issues/239
2024-08-09 20:23:16 -05:00
JK
8c3c58cadc
Merge pull request #236 from rado0x54/bugfix/fix-safeTitle-on-remote-fs
Fix: Only use safeTitle for remote directory name.
2024-08-06 05:26:35 -04:00
JK
e1634556ca
Merge pull request #234 from rado0x54/bugfix/fix-no-itemname
Fix empty [R] name on certain webdav servers.
2024-08-06 05:26:20 -04:00
Martin Riedel
c2cf998d10 fix: use safeTitle instead of title for directory name on remote filesystem. This is a breaking change. Prior names with unsafe titlenames need to be moved manually. 2024-07-27 11:28:34 -04:00
Martin Riedel
f9668895eb fix: Provide fallback for item.name from href if webdav server does not provide "displayname" property. 2024-07-26 07:17:03 -04:00
Jaime J. Denizard
2cf5a22062
Merge branch 'J-D-K:master' into master-1 2024-03-08 23:31:01 -05:00
JK
df89d8b116
Merge pull request #207 from rado0x54/feature/webdav
Webdav Support
2024-02-06 18:48:25 -05:00
Jaime J. Denizard
24644bb743
Fixing typo 2023-08-23 02:51:31 -04:00
Martin Riedel
1aca15ae6e fix: Fixed href assignment for servers without origin. 2023-07-12 09:52:10 -04:00
Martin Riedel
6b8c3da184 fix: Webdav href contains absolute URI the id were not set correctly. 2023-07-11 22:22:03 -04:00
Martin Riedel
ef633b1752 fix: updated Makefile to prioritize local over global imports (gd.h error) 2023-07-10 16:47:41 -04:00
Martin Riedel
f342bf15af feat: PR-related cleanup
- updated build instructions
- added user-agent header to webdav calls
- Generalized popDriveNotActive to popRemoteNotActive
- Updated version identifier presented in app
- Removed some commented code
2023-07-10 15:01:44 -04:00
Martin Riedel
938c52b603 feat: Webdav support for remote storage
- Created common interface that GDrive and Webdav implement (rfs::IRemoteFS)
- Moved shared functionality into shared interface/implementation
- drive.h/.cpp was replaced by remote.h/.cpp
- fld.cpp now gets a copy of RfsItem (former gdIems), because the implementation is not required to retain these (e.g. Webdav does not)
- UI presentation changed from [GD] to [R] (Remote)
2023-07-09 23:13:01 -04:00
Martin Riedel
a893d3fa56 chore: aligned all SDL2 imports to use <SDL2/*> 2023-07-09 23:04:44 -04:00
Martin Riedel
69901a93be feat: Added CMakeLists.txt to support Jetbrains IDE (not for building) 2023-07-09 23:04:44 -04:00
Martin Riedel
aee8d777fc fixed root cause of GDrive config parsing crash 2023-07-07 16:30:02 -04:00
Martin Riedel
96cdb7f488 Ignore dot (.) files
When reading the drive config ignore all files starting with a .
2023-07-05 11:00:27 -04:00
J-D-K
1ac23d2750 Fixes + Revisions to last commit 2023-06-04 20:15:27 -04:00
J-D-K
85bca2d66e Add textureMgr class I wrote at work and prepare a little for auto upload option 2023-05-22 17:13:49 -04:00
J-D-K
6f8393483f Applet mode warning + disable drive/browser to prevent crash 2023-05-17 17:00:18 -04:00
Pf-16
14a2debccd
Update it.txt
Felt like it would be a more accurate translation
2023-05-05 16:09:45 +02:00
J-D-K
83228a2911 Libnx update 2023-04-15 05:37:19 -04:00
qazrfv1234
07b210b0bd
Update zh-TW.txt 2023-03-08 20:57:10 +08:00
J-D-K
09f610b3c9 Attempt to fix GD instructions broken by ancient HTML 2023-02-23 17:10:41 -05:00
JK
d4f16ab540
Merge pull request #176 from SciresM/master
Fix font services type for 16.0.0
2023-02-23 17:00:16 -05:00
Michael Scire
dd3dfa08ae Fix font services type for 16.0.0 2023-02-22 18:37:32 -07:00
J-D-K
d693f8afbb Remove logging from debugging 2023-02-12 09:16:37 -05:00
J-D-K
3e16d635dc Update Drive instructions, load client_id and client_secret from Google JSON instead 2023-02-12 09:14:45 -05:00
JK
3fad76ecaa
Merge pull request #167 from bitest/master
Update zh-CN.txt
2022-11-20 20:14:17 -05:00
bitest
52976d613f
Update zh-CN.txt 2022-11-18 04:40:01 +08:00
J-D-K
f955093746 Temp fixes for non-ASCII system users and drive crash if login canceled. 2022-11-17 14:58:02 -05:00
JK
5049a52529
Merge pull request #166 from qazrfv1234/qazrfv1234-patch-3
Update zh-TW.txt
2022-11-17 13:23:41 -05:00
qazrfv1234
df100e8e32
Update zh-TW.txt 2022-11-17 20:26:10 +08:00
JK
e16e1db34b
Merge pull request #153 from yyoossk/master
jpn update
2022-11-16 18:56:01 -05:00
JK
c6ad8f3bb0
Merge pull request #154 from DDinghoya/patch-1
Update ko.txt
2022-11-16 18:47:19 -05:00
DDinghoya
42dff0b887
Update ko.txt 2022-03-26 14:33:54 +09:00
yyoossk
2b640eed52
jpn update 2022-03-16 21:01:11 +09:00
J-D-K
1f0868de78 Fix drive patch size mismatch 2021-12-21 19:18:58 -05:00
JK
501658cfb9
Merge pull request #145 from SimoCasa/patch-2
Update it.txt
2021-12-21 19:14:12 -05:00
Simo
9a89682ec0
Update it.txt
Update based on new variables in French translations
2021-12-16 23:16:11 +01:00
J-D-K
8d988055d7 A few small fixes and tweaks 2021-12-15 23:29:13 -05:00
J-D-K
2f34ddd3ba Update Drive instructions, readme 2021-12-15 20:21:33 -05:00
J-D-K
b2be2a26fb Remove cfg::driveAuthCode, login on switch instead 2021-12-15 20:04:47 -05:00
J-D-K
00dd0ee6af Updated French 2021-12-07 20:34:34 -05:00
JK
e3d54bed23
Merge pull request #134 from SimoCasa/master
Update it.txt
2021-12-02 20:46:42 -05:00
JK
d1d0564d5b
Merge branch 'master' into master 2021-12-02 20:45:45 -05:00
J-D-K
ca24954079 One last one 2021-12-01 17:55:33 -05:00
J-D-K
f92f8c01ee Try to fix readme with ancient web dev knowledge 2021-12-01 17:52:54 -05:00
J-D-K
72795af2f5 Add Google Drive instructions. Maybe fix them later. 2021-12-01 17:32:50 -05:00
J-D-K
b212d53247 Remove friends only function 2021-12-01 15:01:14 -05:00
J-D-K
df453ee667 Tweak file mode start point, finish restore from drive code 2021-12-01 14:57:06 -05:00
J-D-K
411452658c Update translation files, might need a second look 2021-11-30 15:59:13 -05:00
J-D-K
c29688ea97 Fix config loading, early drive code, threaded file read/write 2021-11-30 15:58:05 -05:00
J-D-K
2fbfe5b62e First(?) readme update, thread race fix 2021-11-15 17:47:12 -05:00
Simo
6ff97ad765
Update it.txt 2021-11-03 23:35:44 +01:00
Simo
6cbf56840a
Update it.txt 2021-11-03 23:32:13 +01:00
Simo
ba07b988b8
Update it.txt 2021-11-03 23:31:09 +01:00
Simo
0ad0dfb598
Delete it.txt 2021-11-03 23:04:14 +01:00
Simo
e49e7bea84
Create it.txt 2021-11-03 23:03:56 +01:00
Simo
09cf058f1b
Delete it.txt 2021-11-03 23:02:55 +01:00
Simo
2015ef490d
Create it.txt 2021-11-03 23:02:22 +01:00
Simo
97789951c3
Update it.txt 2021-11-03 22:48:13 +01:00
J-D-K
093b1bc178 Fix file mode copy file 2021-10-13 17:20:42 -04:00
J-D-K
9ca4c6ff52 Move stuff, fix stuff. 2021-10-12 23:14:04 -04:00
J-D-K
bf15660c2d Code Revisions + Auto extend saves 2021-10-12 21:36:33 -04:00
J-D-K
94b0c800ca Upload new French translation 2021-10-01 21:12:57 -04:00
JK
27e3a26334
Merge pull request #129 from qazrfv1234/qazrfv1234-patch-2
Update zh-TW.txt
2021-09-27 19:19:02 -04:00
JK
5b4a920def
Merge branch 'master' into qazrfv1234-patch-2 2021-09-27 19:17:29 -04:00
qazrfv1234
9392fef706
Update zh-TW.txt 2021-09-26 18:24:07 +08:00
J-D-K
815cdaf513 Revised translation loading, exporting 2021-09-26 02:09:37 -04:00
JK
757c2da90b
Merge pull request #124 from zdm65477730/master
full support for Lang translation in UI
2021-09-25 23:42:08 -04:00
Damien Zhao
18cb1e0742
adjust translations in UI
1. Remove error message translations.
2. Using format string in json file instead of using C++ strings.
3. More accurate naming for translation entries
4. Added the initial strings for newly added translation items in json.
2021-09-21 16:14:08 +08:00
damien zhao
37e8f10aa8
zh-CN.txt mini update 2021-09-05 11:34:05 +08:00
damien zhao
c6c95eb498
min zh-CN.txt update 2021-09-05 11:18:11 +08:00
damien zhao
af210d2f65
full UI with translation 2021-09-05 11:11:26 +08:00
damien zhao
f718f5ac4e
fix lang bug 2021-09-05 01:12:43 +08:00
damien zhao
a60471e888
full ui Lang in lang txt 2021-09-04 23:15:34 +08:00
damien zhao
f3bd18e3df
addSimplified Chinese support 2021-09-04 19:34:57 +08:00
J-D-K
b0a5bd1e7e Add French, code for JP + FR, fix edited menu text not scrolling. 2021-09-03 17:54:50 -04:00
JK
0cea708fb0
Merge pull request #121 from yyoossk/master
jpn
2021-09-03 17:33:55 -04:00
yyoossk
8fe8d788fc
jpn 2021-09-02 08:07:57 +09:00
J-D-K
85a0ce47ee Separate folders for trash 2021-09-01 15:15:53 -04:00
J-D-K
ed77864455 Fix title definition loading 2021-09-01 14:52:58 -04:00
JK
34c1bc50b4
Merge pull request #120 from qazrfv1234/qazrfv1234-patch-1
Update zh-TW.txt
2021-09-01 14:48:48 -04:00
qazrfv1234
51f62b8802
Update zh-TW.txt 2021-09-01 20:23:45 +08:00
J-D-K
a2bf9a8e64 Fix legacy config loading 2021-08-31 18:09:39 -04:00
J-D-K
fe5656b490 Fix dump all bug 2021-08-26 18:19:03 -04:00
J-D-K
a88de19023 Blacklist editor in settings finally. 2021-08-24 23:33:42 -04:00
J-D-K
213f11a274 Remove warning 2021-08-21 22:24:43 -04:00
J-D-K
c6689de00e Merge branch 'newUI' 2021-08-21 22:18:58 -04:00
J-D-K
275ae7dbdc Export new en-US.txt 2021-08-21 22:13:12 -04:00
J-D-K
8da1924ed9 BCAT creation fix, JKSV dir zipping back, Auto-Name setting, delete all backups, zip path trimming, use heap for recursive fake threads 2021-08-21 22:04:01 -04:00
J-D-K
b71eccdd6a File mode properties added 2021-08-18 18:49:34 -04:00
J-D-K
3491d7f09a Fix thing I forgot to remove 2021-08-17 22:39:06 -04:00
J-D-K
1c814f5172 Merge branch 'newUI' of https://github.com/J-D-K/JKSV into newUI 2021-08-17 22:26:29 -04:00
J-D-K
92e780d284 Use Free Bird's CPU speed definitions, Fix ZIP headers, Mass Backup in user options 2021-08-17 22:25:06 -04:00
JK
de6e445740
Merge pull request #118 from zand/newUI-iss114
New UI iss114
2021-08-16 22:00:24 -04:00
J-D-K
2b6f91e20f Update readme, add legacy config loading, new translation format, remove curUser and curData definition shortcuts 2021-08-16 21:58:18 -04:00
Chris Butler
2ebe81556d Refactored swkbdDictWordCreate 2021-08-09 02:02:37 -04:00
Chris Butler
6442209cd6 Fix buffer overflow in swkbdDictWordCreate 2021-08-09 02:02:28 -04:00
J-D-K
b74349f7c0 New config, restructure some stuff for stability and to fix issues 2021-08-06 22:21:24 -04:00
J-D-K
730f7ffbca Fix file mode if used as root dir, add scroll for long menu options 2021-08-04 22:56:54 -04:00
J-D-K
93e9fade66 Alpha sort save menus, tweak/fix menu code(hopefully), tweak thread proc, fix auto backup, add back a few more extras 2021-08-03 22:19:42 -04:00
J-D-K
1001eeb8de File Mode almost done. 2021-08-01 22:02:06 -04:00
J-D-K
92d0f3f74e Get DOOM Cache to show up in menu, Start working on Extras + File Mode 2021-08-01 00:28:44 -04:00
J-D-K
1e3e01a810 Split FS/Threaded funcs, make restore check for empties before wiping, start working on extras (Only delete update works now) 2021-07-28 22:23:08 -04:00
J-D-K
e445181e0c Fix blacklisting, make menus scroll(may be buggy) 2021-07-27 21:58:04 -04:00
J-D-K
a50a43870d Fix makefile for Linux (I hope) 2021-07-24 21:43:44 -04:00
J-D-K
5ae0d587c3 Tweak dialogs, fix white theme, add CPU boost for ZIP files 2021-07-24 21:34:51 -04:00
J-D-K
d18a048471 Increase stack size and fix overwriting crash 2021-07-23 06:49:12 -04:00
J-D-K
9227c7a3f1 Progress bars, split some ui classes, threaded confirm, better thread safety(I think) 2021-07-22 18:44:47 -04:00
J-D-K
d18204733d Hope the headache was worth it 2021-07-21 00:11:17 -04:00
J-D-K
0459fd363b Add queue to fix threading graphics issues 2021-07-19 20:13:03 -04:00
J-D-K
eaf6bed3de Newer threading stuff, trash bin, extend/keyboard length fix 2021-07-19 19:51:33 -04:00
J-D-K
d40cd33872 Add Folder definitions to ttlOptsMenu, fix some things 2021-07-17 00:51:54 -04:00
J-D-K
f12f81b67f Add animation scaling option, revise menu event handling 2021-07-16 21:58:15 -04:00
J-D-K
cf832a0f91 Updated journal space fix/handling, updated scroll logic, fix a typo 2021-07-15 19:14:01 -04:00
J-D-K
258667a9e3 Restore config, fix some issues 2021-07-14 23:01:46 -04:00
J-D-K
973e1dab0f Add save extending, start getting settings + extras back 2021-07-13 21:48:43 -04:00
J-D-K
750d1a2b24 updated 2021-07-12 19:24:11 -04:00
J-D-K
f75cf63cf7 update/fix some stuff 2021-07-11 02:47:45 -04:00
J-D-K
3f8a53a3d8 fix warning b4 i pass out 2021-07-09 23:10:59 -04:00
J-D-K
6615edb182 Update w/ warning 2021-07-09 23:09:51 -04:00
J-D-K
6fdd7d33e7 WIP UI Update 2021-07-09 23:05:19 -04:00
J-D-K
3e89939308 Add language override bool 2021-06-14 23:00:03 -04:00
J-D-K
b5037abd83 Restore color pulse, fix up zh-TW line breaking rules, fix load screen 2021-06-14 22:26:36 -04:00
JK
cf8da7cb1c
Merge pull request #100 from iguneesu/master
Add JKSV icon art asset sources
2021-06-09 14:38:06 -04:00
Iguneesu
22d7949c23
Add JKSV icon art asset sources 2021-06-08 19:25:16 +02:00
J-D-K
e34a3f36c7 >>WIP Early<< SDL2 GFX 2021-06-02 17:05:57 -04:00
J-D-K
a02b3e82c3 Fix a couple things 2021-05-27 17:56:04 -04:00
J-D-K
6c95ffc0f7 Update readme + updated traditional chinese 2021-05-17 15:27:05 -04:00
JK
9e9a716546
Merge pull request #85 from ca1e/patch-1
Update README.MD
2021-05-17 14:35:06 -04:00
JK
63cba6364c
Merge pull request #87 from ca1e/patch-2
Update zh-CN.txt
2021-05-17 14:34:54 -04:00
J-D-K
5080aaeac1 New Snap/Journal space fix 2021-05-05 19:59:40 -04:00
NPC-C83H
8663314826
Update zh-CN.txt 2021-03-15 14:37:16 +08:00
NPC-C83H
bda3dfe473
Update README.MD
required package-names typo for libjson-c(without `-` between 'lib' and 'json-c')
2021-02-27 10:09:54 +08:00
J-D-K
97aa90034a Quick and dirty HID update 2020-12-20 01:20:35 -05:00
J-D-K
abfa13fbc1 Add path filtering(Not tested thoroughly) 2020-11-01 16:31:16 -05:00
J-D-K
11a382794b Add sort options 2020-08-27 16:11:18 -04:00
J-D-K
50b195f398 Add play time 2020-08-04 19:05:13 -04:00
J-D-K
5c6e53e287 Get SD card saves showing up right. 2020-07-22 19:29:46 -04:00
J-D-K
555d36e5d0 Push svi reading code, remove free space checking 2020-07-17 20:36:11 -04:00
J-D-K
b04d33eee8 revert gfx derp 2020-07-14 17:28:17 -04:00
J-D-K
ad6b6faedd Make jpeg errors not call exit() 2020-07-14 17:23:33 -04:00
J-D-K
d8cbb3401a Add online update code, alpha mask for icons, optimize gfx a bit better 2020-07-09 23:24:57 -04:00
J-D-K
5df84e3ad7 Fix/Update curlfuncs 2020-06-28 20:17:07 -04:00
J-D-K
3969716bb9 Clean up some stuff. 2020-06-23 22:25:07 -04:00
J-D-K
2918aedff9 Revise stuff 2020-06-13 14:13:00 -04:00
J-D-K
fa257562f4 Fix some text UI stuff, get rid of gross if. 2020-06-10 17:48:34 -04:00
J-D-K
40131cd3f9 -Ofast alpha blending, Fix up folder strings 2020-06-09 19:28:56 -04:00
J-D-K
4e4ca56f79 Update Readme + date 2020-06-07 20:15:48 -04:00
J-D-K
9e87758298 Add a few things to readme 2020-06-07 19:09:07 -04:00
J-D-K
8ddc989152 Make Dump All support zip option 2020-06-07 12:53:16 -04:00
J-D-K
122a4e9c31 move del 2020-06-07 12:23:18 -04:00
J-D-K
8a4e68e3ff Add ZIP restore 2020-06-07 12:05:58 -04:00
J-D-K
633a97a568 Add drawTextf, drawTextfWrap, Add ZIP export (import later) 2020-06-06 21:58:27 -04:00
J-D-K
749c8fd654 Stop loading icons i don't use 2020-06-04 21:55:00 -04:00
J-D-K
4420e7c164 Use std::unordered_map for icons instead of class/vectors 2020-06-04 19:59:15 -04:00
J-D-K
370ecc0e36 Update readme for recent changes 2020-06-04 16:42:36 -04:00
JK
4e86fa0d17
Merge pull request #40 from JamePeng/add-new-external-string-0605
Add new external strings for zhCN
2020-06-04 16:22:46 -04:00
JamePeng
8ddda9363f Add new external strings for zhCN 2020-06-05 02:56:43 +08:00
J-D-K
c9bba1eca5 Add new external strings, fix up some things. 2020-06-04 11:03:14 -04:00
J-D-K
97b37b1351 Add definable paths/safe titles. 2020-06-03 12:43:06 -04:00
J-D-K
657b41111f Fix up readme, add spaces to traditional chinese for line breaking, fix up some other stuff 2020-06-02 22:38:11 -04:00
J-D-K
4e6fa82e47 Fix user icon memory leak 2020-06-01 13:27:55 -04:00
JK
5a7dc0e0e5
Merge pull request #37 from JamePeng/zhcn-x-default-update
update option text in zh-CN translation file
2020-05-29 22:25:09 -04:00
JamePeng
058f030ffd update option text in zh-CN translation file 2020-05-30 09:42:29 +08:00
J-D-K
9d1008c8d4 Make config save after exiting menu, add restore default. Fix typo in traditional chinese file. 2020-05-29 17:54:37 -04:00
JK
996bd3be43
Merge pull request #36 from JamePeng/update-zhcn-file
Update Simplified Chinese translation file and Add sysLang code for ZHCN
2020-05-29 17:28:55 -04:00
JamePeng
0a0a1de683 Add sysLang code for ZHCN 2020-05-30 01:12:39 +08:00
JamePeng
b29467184c Update Simplified Chinese translation file 2020-05-30 01:11:23 +08:00
J-D-K
2c643e3a8b Add code for and push Traditional Chinese translation file 2020-05-27 19:54:54 -04:00
J-D-K
93cde450c2 uncomment us text skip (oops) 2020-05-26 20:41:16 -04:00
J-D-K
da120ade39 Add quote reading to dataFile, add more to translatable strings 2020-05-26 20:40:32 -04:00
J-D-K
c6e90bb85a Handle all save data types 2020-05-25 14:21:24 -04:00
J-D-K
9d39d4e586 fix typo 2020-05-24 23:22:40 -04:00
J-D-K
41da3c8a92 Fix favorites, try to not make copies of users in memory 2020-05-24 22:52:19 -04:00
J-D-K
7cde0a12ba Clean up data 2020-05-24 19:48:13 -04:00
J-D-K
bdd1a1d88d Remove namespace from source files, add more text to translation text, add place holder files for translations, make folder menus share functions 2020-05-22 22:17:33 -04:00
J-D-K
2dc96be0b8 update readme 2020-05-20 16:27:00 -04:00
J-D-K
de57365d1e Add asctime for naming, add skip user option 2020-05-20 16:18:44 -04:00
J-D-K
7ba9cbb97a Add loading screen, fix some things. 2020-05-19 20:39:52 -04:00
J-D-K
69f3891d24 Add option to directly use FS commands. 2020-05-19 18:01:55 -04:00
J-D-K
77918c8387 Change user select, finally rename colors. 2020-05-16 00:01:31 -04:00
J-D-K
197b0b0c67 Fix missing button prompts 2020-05-15 13:54:16 -04:00
J-D-K
8ccba85d7c Fix rescan titles 2020-05-15 01:10:13 -04:00
J-D-K
d872f2f904 Remove touch controls, add support for system saves tied to accounts, add option to enable system save writing, bugs! 2020-05-15 01:00:47 -04:00
J-D-K
9f71b479f7 Add way to bail from batch backup, change button glyph macro, add force mount option, shorten hold time 2020-05-14 19:30:51 -04:00
J-D-K
cbea40e42e Add back RomFS opening to extras, add free space checks 2020-05-08 02:30:41 -04:00
J-D-K
1e03cbaf85 fix readme 2020-04-30 21:00:24 -04:00
J-D-K
052b24b1be Add defineable path 2020-04-30 20:59:33 -04:00
J-D-K
d0a6aed45d Add new screenshot 2020-04-26 22:53:11 -04:00
J-D-K
ac4375465e Shorten hold time, add translation support for important strings, remove applet detection 2020-04-26 22:30:58 -04:00
J-D-K
0205d76df4 Thread file copying 2020-04-22 20:43:23 -04:00
J-D-K
e18c381a05 Add quick guide to readme. 2020-04-18 23:00:37 -04:00
J-D-K
53d839101c fix typo 2020-04-17 13:13:52 -04:00
J-D-K
5d13264e57 Add erasing + rescan 2020-04-17 13:05:59 -04:00
J-D-K
3b4b125826 Add delete save data option button. Also, maybe future translation support. 2020-04-15 13:24:15 -04:00
J-D-K
60d70b53bb Add options for holding 2020-04-14 14:23:00 -04:00
J-D-K
4716225ce6 Fix up some stuff, use va_list for log 2020-04-10 01:50:18 -04:00
J-D-K
4d5e1bba06 Clean up a little more 2020-04-09 14:16:32 -04:00
J-D-K
74e89c914b Fix touch not setting info string 2020-04-08 17:18:20 -04:00
J-D-K
099a334c2f Clean some stuff up 2020-04-08 00:29:17 -04:00
J-D-K
0729edc35c Start looking closer/Cleanup(Maybe) 2020-04-07 23:14:40 -04:00
J-D-K
f7085633a5 Add missing auto-back to text UI 2020-04-06 22:20:22 -04:00
J-D-K
fa86675563 Tweak textboxes 2020-04-06 19:14:36 -04:00
J-D-K
8f6290d9b3 Fix up some stuff 2020-04-05 22:13:46 -04:00
J-D-K
29e0f978d5 Fix typo I never noticed. 2020-04-04 21:13:21 -04:00
J-D-K
9628654d80 Fix something 2020-04-04 16:08:17 -04:00
J-D-K
2a5000cb91 Add Favorites 2020-04-03 17:12:51 -04:00
J-D-K
21ce0d9d82 Add options to text users (oops) 2020-04-02 20:21:32 -04:00
J-D-K
6b555ad656 Add options menu 2020-04-02 20:16:16 -04:00
J-D-K
5abd0f0a3a Use stdio instead of fstream, remove NAND backup options, remove kaboom guy(RIP brave warrior), skip keyboard in applet 2020-04-02 15:49:46 -04:00
J-D-K
0514cd2c59 Add hold. Fix more stuff I missed. 2020-03-27 21:33:14 -04:00
J-D-K
6bcd0f2e83 Fix up some stuff 2020-03-27 19:17:26 -04:00
J-D-K
c71ebc96fc Add freebird popup + user change in titles 2020-03-24 01:36:57 -04:00
J-D-K
e8d8272613 Fix drawTextWrapped and update colors 2020-03-23 23:07:29 -04:00
J-D-K
8e399797d9 Change Device icon to Switch glyph 2020-03-22 22:23:42 -04:00
J-D-K
15f7918a89 Use extended font instead on pngs 2020-03-22 21:18:40 -04:00
J-D-K
328fcd5c6c begin updating ui with freebird assets 2020-03-21 22:04:05 -04:00
J-D-K
3bc330fd05 Update date 2020-03-21 18:54:02 -04:00
J-D-K
575bcbd94a Rearrange Icons 2020-03-21 18:48:24 -04:00
J-D-K
03b09f550a remove debug stuff 2020-03-19 15:46:23 -04:00
J-D-K
68093bacae Change icon names 2020-03-19 15:43:24 -04:00
J-D-K
32ca2b41e5 Account fix 2020-01-07 14:49:23 -05:00
J-D-K
1f4bb8d0f9 Fix loading order 2020-01-06 16:55:16 -05:00
J-D-K
8e04b7a48a fix stuff up a little 2020-01-06 12:03:51 -05:00
J-D-K
e708f02b6f Working on it 2020-01-06 03:27:10 -05:00
JK
30c2c5e651
Merge pull request #10 from octopuserectus/pr-libnx
Some libnx changes
2020-01-06 00:19:38 -05:00
octopuserectus
187dc85fa7 Some libnx changes
Please check
2020-01-05 19:42:13 +01:00
J-D-K
29c9d3a3d6 Push Device save code 2019-12-20 18:57:54 -05:00
J-D-K
669bb423b9 Builds again 2019-11-27 17:01:17 -05:00
J-D-K
bdd7558e41 Generate Icons + Everything enabled by default 2019-05-16 22:02:23 -04:00
J-D-K
b7edbad6cd Add BCAT 2019-05-12 23:57:56 -04:00
J-D-K
7027f81aa2 Fix old swkbd stuff 2019-04-26 15:47:25 -04:00
J-D-K
f207bfab48 Finally include romfs opening code 2019-04-18 12:24:12 -04:00
J-D-K
512889a1a5 offscreen drawing 2019-04-17 16:30:33 -04:00
J-D-K
bc0a99cab9 Fix date 2019-01-08 16:45:58 -05:00
J-D-K
235c03a452 Tweak dictionary entries 2019-01-08 16:33:43 -05:00
J-D-K
6d48ff62c0 Add title abbreviation 2019-01-08 16:20:46 -05:00
J-D-K
9551d85e0f Fix swkbd for nro + dictionary 2019-01-08 15:44:10 -05:00
J-D-K
bd78f186aa Switch to swkbd + update gfx 2018-12-23 14:43:55 -05:00
J-D-K
fb02227965 Fix committing to non-saves 2018-12-19 23:07:48 -05:00
J-D-K
678e9d164d Add dir props + fix menu bug 2018-12-19 18:15:33 -05:00
J-D-K
8bc8a4631a Readme update 2018-12-19 00:27:55 -05:00
J-D-K
fc03927552 Missed stuff 2018-12-16 20:52:47 -05:00
J-D-K
edff6f7dac UI Tweaking 2018-12-16 20:44:24 -05:00
J-D-K
cc8f91dee3 Terminate by process ID 2018-12-06 21:52:19 -05:00
J-D-K
581db1ea59 Update 2018-12-03 23:26:13 -05:00
J-D-K
1a33cd9ddc Layout Update 2018-11-07 11:51:02 -05:00
J-D-K
4e592a4c00 Add option to delete downloaded updates 2018-10-14 20:56:45 -04:00
J-D-K
7329402523 Update readme 2018-10-01 19:51:53 -04:00
JK_
8ff86938da
Merge pull request #3 from igniscitrinus/jksv-icon
Create icon.jpg for project
2018-10-01 19:41:36 -04:00
Iguniisu
ba68d6fccd
Fix icon.jpg libjpeg-turbo compatibility
Did not render correctly in nx-hbmenu previously, working correctly now
2018-10-01 15:55:45 +02:00
Iguniisu
1a16b3ec2c
Update icon.jpg
- Changed left half color
- Smaller overall margins
- Downsized to a more sensible 256x256
2018-10-01 13:54:33 +02:00
Iguniisu
d5959ea532
Add icon.jpg to project
Adds a vaguely thematically-appropriate icon to the main project.
2018-10-01 13:40:10 +02:00
J-D-K
bf421adcfa Clean up a little 2018-09-26 21:37:32 -04:00
J-D-K
e10123a437 Tidy up a little 2018-09-22 17:19:57 -04:00
J-D-K
6f5bd864f2 Remove other thing I missed 2018-09-22 16:39:50 -04:00
J-D-K
4c65f6e00c Remove slash I missed 2018-09-22 16:37:41 -04:00
JK_
4562e7f14d
Correct readme 2018-09-15 14:15:52 -04:00
J-D-K
14c6777246 Small update 2018-09-15 13:20:34 -04:00
J-D-K
f5465b2152 More small tweaks/fixes 2018-09-03 15:32:13 -04:00
J-D-K
fceaf2e21f Update readme + small stuff 2018-09-01 22:28:51 -04:00
J-D-K
89ab57b8ea Add DevMenu back and Bis storage exploring 2018-08-27 23:21:57 -04:00
J-D-K
d6517b9903 Mostly UI touches 2018-08-26 00:12:47 -04:00
J-D-K
2ab16b9e04 Minor stuff 2018-08-23 23:15:53 -04:00
J-D-K
6572e49e91 More text menu tweaks 2018-08-18 23:47:04 -04:00
J-D-K
1effe15da2 WIP New text wrapping 2018-08-18 23:22:07 -04:00
J-D-K
da7944aaa0 Add conf to blacklist 2018-08-15 01:51:18 -04:00
J-D-K
f999171da6 Add blacklist to cls 2018-08-12 01:40:10 -04:00
J-D-K
22bd9d5b9f Add title blacklist 2018-08-06 18:50:30 -04:00
127 changed files with 14093 additions and 4221 deletions

136
.clang-format Normal file
View File

@ -0,0 +1,136 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: true
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 1000
PointerAlignment: Right
ReflowComments: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
...

9
.clang-tidy Normal file
View File

@ -0,0 +1,9 @@
---
Checks: 'clang-analyzer-*,cppcoreguidelines-*,modernize-*,bugprone-*,performance-*,readability-*,readability-non-const-parameter,misc-const-correctness,misc-use-anonymous-namespace,google-explicit-constructor,-modernize-use-trailing-return-type,-bugprone-exception-escape,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-avoid-magic-numbers,-bugprone-easily-swappable-parameters,-cppcoreguidelines-non-private-member-variables-in-classes'
WarningsAsErrors: ''
HeaderFilterRegex: ''
CheckOptions:
- key: readability-magic-numbers.IgnoredFloatingPointValues
value: '0.0;1.0;100.0;'
- key: readability-magic-numbers.IgnoredIntegerValues
value: '0;1;2;3;4;5;6;7;8;9;'

11
.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
# Set the default behavior for all files.
* text=auto eol=lf
# Normalized and converts to native line endings on checkout.
*.c text
*.cc text
*.cxx
*.cpp text
*.h text
*.hxx text
*.hpp text

15
.gitignore vendored
View File

@ -1,3 +1,16 @@
build/
.vscode/
*.cbp
*.layout
*.layout
.editorconfig
.idea/
cmake-build-debug/
# build artifacts
JKSV.elf
JKSV.lst
JKSV.nacp
JKSV.nro
JKSV.nso
JKSV.pfs0

50
CMakeLists.txt Normal file
View File

@ -0,0 +1,50 @@
# This is mainly for IDE Support (CLION), not for building (use Makefile directly).
cmake_minimum_required(VERSION 3.8)
project(JKSV)
set(CMAKE_CXX_STANDARD 17)
set(SOURCE_FILES
src/cfg.cpp
src/curlfuncs.cpp
src/data.cpp
src/fs.cpp
src/gd.cpp
src/gfx.cpp
src/main.cpp
src/rfs.cpp
src/type.cpp
src/ui.cpp
src/util.cpp
src/webdav.cpp
src/fs/dir.cpp
src/fs/remote.cpp
src/fs/file.cpp
src/fs/fsfile.c
src/fs/zip.cpp
src/gfx/textureMgr.cpp
src/ui/ext.cpp
src/ui/fld.cpp
src/ui/fm.cpp
src/ui/miscui.cpp
src/ui/sett.cpp
src/ui/sldpanel.cpp
src/ui/thrdProc.cpp
src/ui/ttl.cpp
src/ui/ttlview.cpp
src/ui/uistr.cpp
src/ui/usr.cpp)
# Specify external includes here
include_directories(./inc)
include_directories(./inc/fs)
include_directories(./inc/gfx)
include_directories(./inc/ui)
include_directories($ENV{DEVKITPRO}/devkitA64/aarch64-none-elf/include)
include_directories($ENV{DEVKITPRO}/devkitA64/lib/gcc/aarch64-none-elf/10.1.0/include)
include_directories($ENV{DEVKITPRO}/libnx/include)
include_directories($ENV{DEVKITPRO}/portlibs/switch/include)
include_directories($ENV{DEVKITPRO}/portlibs/switch/include/freetype2)
add_executable(JKSV ${SOURCE_FILES})

42
JKSV_Settings_ENG.MD Normal file
View File

@ -0,0 +1,42 @@
# JKSV's Settings menu options and what they do:
1. **Empty Trash Bin**: Empties the trash folder of all backups moved to it. Running this now and then is important if you choose to use the trash bin option. It is enabled by default.
2. **Check for Updates**: Checks JKSV's Github release page for any new updates and downloads it for you.
3. **Set JKSV Output Folder**: Changes the folder in which JKSV stores your save backups. Remember to end the path with a slash so this works properly.
4. **Edit Blacklisted Titles**: Opens a menu that allows you to select and remove titles that are hidden in JKSV's title selection menu.
5. **Delete All Save Backups**: Wipes JKSV's folder clean for you to start fresh.
6. **Include Device Saves With Users**: Includes device type saves (Animal Crossing, for example) with users so finding them is easier and also makes it easier to differentiate and have multiple saves for different users on the same console.
7. **Auto Backup On Restore**: Makes JKSV automatically create a backup before restoring another backup just in case.
8. **Auto Name backups**: Automatically names backups and skips the keyboard entirely.
9. **Overclock/CPU Boost**: Overclocks the CPU when exporting ZIP archives. This has little effect on the speed of compression so it's recommended to leave it off. It's being removed in the rewrite anyway for having almost no impact.
10. **Hold to Delete**: Whether or not you would like JKSV to force you to hold A to delete a backup.
11. **Hold to Restore**: Whether or not you would like JKSV to force you to hold A to restore a backup.
12. **Hold to Overwrite**: Whether or not you would like JKSV to force you to hold A to overwrite a backup on your SD card.
13. **Force Mount**: This controls whether or not JKSV displays everything it finds on your Switch or only the saves it can successfully open and mount on boot.
14. **Account System Saves**: This controls whether or not JKSV displays system save data that has an account user ID associated with it or not. This is different than normal system save data because that does not have an account user ID associated with it.
15. **Enable Writing to System Saves**: This enables writing and restoring to system save data and writing to the BIS partitions in the file browser mode. This is **dangerous** and should normally be left off. **Any damage incurred with this option on is your own fault.**
16. **Use FS Commands Directly**: Uses the Switch's FS file functions instead of LibNX's fs_dev and stdio. This can _sometimes_ resolve odd issues.
17. **Export Saves to Zip**: Uses ZIP files for backing up saves instead of unpacked files in folders. This has the benefit of higher compatibility with various edge-case saves with non-ASCII filenames which the SD card can't handle.
18. **Force English to be Used**: Overrides the detected language and uses the default English strings.
19. **Enable Trash Bin**: Enables moving backups deleted to the `_TRASH_` folder instead of deleting them permanently.
20. **Title Sorting Type**: Changes the way titles are sorted and displayed.
22. **Animation Scale**: Changes the transition speed for animated parts of the UI. One being instant, 8.0 being the slowest _I normally allow_.

View File

@ -32,31 +32,31 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
TARGET := JKSV
BUILD := build
SOURCES := src src/ui
SOURCES := src src/ui src/fs src/gfx
DATA := data
INCLUDES := inc
INCLUDES := inc inc/ui inc/fs inc/gfx
EXEFS_SRC := exefs_src
APP_TITLE := JKSV
APP_AUTHOR := JK_
APP_VERSION := 08/05/2018
APP_AUTHOR := JK
APP_VERSION := 11.5.2024
ROMFS := romfs
ICON := icon.jpg
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
override CFLAGS += -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
override CFLAGS += $(INCLUDE) -D__SWITCH__
override CFLAGS += `sdl2-config --cflags` `freetype-config --cflags` `curl-config --cflags`
override CFLAGS += -g -Wall -O2 -ffunction-sections -ffast-math $(ARCH) $(DEFINES)
override CFLAGS += $(INCLUDE) -D__SWITCH__ `freetype-config --cflags`
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := `freetype-config --libs` -lpng -ljpeg -lnx
LIBS := `sdl2-config --libs` `freetype-config --libs` `curl-config --libs` -lSDL2_image -lwebp -lpng -ljpeg -lz -lminizip -ljson-c -ltinyxml2 -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing

110
README.MD
View File

@ -1,22 +1,108 @@
# JKSV
WIP save manager for Switch. Started to get familiar with libnx and test my basic, _mostly_ self written graphics drawing file. **This is still a WIP. Nothing is final yet!**
JK's Save Manager Switch Edition.
**A custom 1280x720 background can be used by placing a JPEG named 'back.jpg' in the JKSV folder ex: "_sdmc:/JKSV/back.jpg_"**
<img src="https://i.imgur.com/yLcTPzt.jpg"/>
**A custom font can be loaded by placing a TTF file in the folder named 'font.ttf' ex: "_sdmc:/JKSV/font.ttf_"**
## Info
This started as a simple, straight port of my 3DS save manager I publicly released in 2016. Despite not originally wanting to take it too far, I continued working on it for fun when I can. Currently it can:
1. Dump and restore account save data.
2. Dump and restore device saves shared by all users (Games such as Animal Crossing).
3. Dump and restore BCAT Data.
4. Dump and restore cache Saves.
5. Dump system save data.
* Dumping this data is always enabled, but writing back needs to be enabled from the options menu. Writing to this can be very dangerous.
* Processes can be terminated from the Extras menu allowing you to open even more of these and explore more.
6. Export save data to folders like the orignal or compressed zip files to save space.
7. Upload and download save backups to [Google Drive](./REMOTE_INSTRUCTIONS.MD#gdrive) if it is configured.
8. Upload and download save backups to [WebDav](./REMOTE_INSTRUCTIONS.MD#webdav) if it is configured.
9. Create save data so the user no longer needs to boot games to import saves.
* Titles can be rescanned from the Extras menu. For example, if you insert a game card while JKSV is open, rescanning will load and add it to the save creation menu(s).
10. Export and use SVI files to create save data for titles not installed on your system. For games that detect other game saves to unlock content.
* SVI files are simply the title ID, NACP, and icon packed into a file. Placing them in `JKSV/svi` will load them as if they are any other game on your switch. They will appear in the save creation menus with the rest.
11. Extend save data containers to any size the user wants or automatically if the save cannot fit into the current one.
12. Delete save data from the system.
13. Reset save data as if the game was never launched.
14. Display stats and information about the game/save: Play time, launch count, title ID (TID), save ID(SID)/name of save file on user nand partition, etc.
15. Open and explore bis storage partitions via the Extras menu
* BIS Storage is opened inside a basic filebrowser. The partition's listing is on the left. Your SD is on the right.
* Only copying to SD and file properties work on BIS partitions. Writing to and deleting are disabled unless enabled like system save data.
16. Misc Extras:
* Ability to remove downloaded firmware updates from NAND. This is located under Extras.
* Terminating processes by [ID](https://switchbrew.org/wiki/Title_list#System_Modules). Allowing you to dump normally unopenable system archives.
* Mount by System Save [ID](https://switchbrew.org/wiki/Flash_Filesystem#System_Savegames). Normally used when the terminated process makes JKSV unable to rescan titles without the Switch crashing.
* Mount and open RomFS of process the homebrew menu takes over (if launched as NRO).
* Hold R while opening a game or applet with Atmosphere so the homebrew menu loads. Open JKSV and press minus and select **Mount Process RomFS**. The romfs of the app should appear in the browser along with your SD on the right.
More customization in the future, maybe.
**NOTE: Some features may require building JKSV from source. I am extremely picky and only release when I am satisfied and sure things work 100% as expected.**
<img src="https://dl.dropboxusercontent.com/s/wk5igkkdt89ytob/JKSV.jpg" alt="JKSV" width="240"></img>
<img src="https://dl.dropboxusercontent.com/s/xaqycf724p8ut2n/JKSV_3.jpg" alt="JKSV" width="240"></img>
<img src="https://dl.dropboxusercontent.com/s/e5qvbiz88hsrtbc/JKSV_2.jpg" alt="JKSV" width="240"></img>
## Quick Guide
1. Main/User Menu
* A Selects the currently highlighted user and allows you to browse their titles.
* Y Dumps the save data for all users.
* X opens the sub menu of options for the currently highlighted user:
* Dump All for [X] dumps all of the saves for the highlighted user.
* Create save data opens a list of games found on your switch and will allow to create save data for them without needing to start the games.
* SVI files located in `JKSV/svi` will also be loaded and added to this list. These are to create save data for games not currently on the system. This is for games that search for other saves to unlock extra content.
* Cache saves require a cache index number to be created. This information can be found under information for a cache save when being exported.
* Create All Save Data creates save data for every title on your system for the selected user.
* Delete All Save Data deletes all save data for the selected user. This is permanent.
* Settings and Extras below.
2. Title/Game Select
* A selects and opens the backup menu.
* Adding `.zip` to the end of your file name will force zip regardless of whether it's enabled or not.
* Y Favorites a title and pushes it to the top of your games.
* L and R jump forward down your games.
* X opens the title options menu:
* Information displays stats about the game for the current user.
* Blacklist adds the title to a list that is not displayed.
* Change Output Folder changes the folder to which the game's saves are written.
* Open in File Mode opens the save in a basic file browser.
* Delete All Save Backups deletes all of the backups for the current title.
* Reset Save Data wipes the save clean as if it was never run.
* Delete Save Data deletes the save data for the title from the system. This is the same as doing it from the Switch's data management setting.
* Extend Save Data extends the container for the current title. This is also done automatically if the save being imported is too large for the container.
* Different games have different limits. Most games do not need this at all. A few will take advantage of a larger container, others extend theirs at times and will need larger containers than created by default.
* Export SVI exports the data needed to create save data for the current title to `JKSV/ncap/[TID].svi`. This is just the title ID, NACP, and icon of the title. These can be used to create save data for games not installed on your system.
# Building:
4. File Mode
* A opens directories.
* B goes back up a folder if possible.
* X opens a small menu of options for files and directories:
* Copy to [X] - Copies the currently selected item to the location opened on the other panel. Selecting the first `.` will use the directory opened as root to copy.
* Delete deletes the currently selected item.
* Rename renames the currently selected item.
* Make Dir creates a folder.
* Properties gets file size and directory size.
* ZL or ZR Change the controlled menu.
5. Settings
* Empty Trash Bin deletes all backups inside the `_TRASH_` folder. The trash bin feature can be disabled further down.
* Check For Updates checks github for updates to JKSV. This currently only updates the NRO release. Maybe NSP later.
* Set JKSV Save Output Folder allows you to set where JKSV's working directory is. Files and folders should be relocated for you.
* Edit Blacklisted Titles allows you to removed titles blacklisted from being shown.
* Delete All Save Backups wipes JKSV's folder of all save backups.
5. Extras
* SD To SD Browser opens the filebrowser with your SD open in both panels
* BIS: [X] opens partition [X] in the filebrowser.
* Remove Update deletes system updates downloaded from Nintendo and asks to reboot the system to get rid of the update nag.
* Terminate Process asks for a title ID to terminate.
* Mount System Save asks for the save ID to mount. This is for when JKSV is unable to rescan with a process terminated.
* Rescan Titles reloads save data information. This can be used to reload after a process is terminated or when options are changed.
* Mount Process RomFS opens the title's romfs that is taken over to launch the homebrew menu. This only works as an NRO. The NSP will only open JKSV's own RomFS.
* Backup JKSV folder writes the entire JKSV folder to a ZIP archive and places it in your JKSV folder.
**NOTE**: Press Plus to Exit JKSV. JKSV saves all config, favorites, and the blacklist when exited. Pressing the home button and closing that way will not allow this to take place.
## Building:
1. Requires [devkitPro](https://devkitpro.org/) and [libnx](https://github.com/switchbrew/libnx)
2. Requires switch-freetype, libpng, and libjpeg-turbo
2. `dkp-pacman -S switch-curl switch-freetype switch-libjpeg-turbo switch-tinyxml2 switch-libjson-c switch-libpng switch-libwebp switch-sdl2 switch-sdl2_gfx switch-sdl2_image switch-zlib`
# Credits and Thanks:
* [shared-font](https://github.com/switchbrew/switch-portlibs-examples) example by yellows8
* Authors of switch-examples for account and save mounting code.
## Credits and Thanks:
* [shared-font](https://github.com/switchbrew/switch-portlibs-examples) example by yellows8 for loading system font with Freetype. All other font handling code (converting to SDL2, resizing on the fly, checking for glyphs, cache, etc) is my own.
* [Iguniisu](https://github.com/igniscitrinus) for the icon.
* Uses graphics from [Twemoji](https://github.com/twitter/twemoji) covered by the [Creative Commons License](https://creativecommons.org/licenses/by/4.0/legalcode)
* [Leo](https://github.com/qazrfv1234) For the Traditional Chinese translation
* [JamePeng](https://github.com/JamePeng) For the Simplified Chinese translation.

58
REMOTE_INSTRUCTIONS.MD Normal file
View File

@ -0,0 +1,58 @@
# Remote Storage
⚠️ **WARNING** ⚠️: Breaking change with Version 07.27.2024. See [here](#remote-changelog)
## <a name="gdrive"></a><center> How to use Google Drive with JKSV </center>
**USING GOOGLE DRIVE WITH JKSV CURRENTLY REQUIRES BUILDING IT YOURSELF. I AM VERY BUSY LATELY AND THINGS WILL ONLY GET FINISHED WHEN I HAVE TIME. Thanks, sorry for yelling.**
**NOTE: As of Feb 2023, JKSV now uses the JSON downloaded from Google directly instead of editing JKSV's configuration file.**
**Google only allows unverified apps to have up to 100 test users. Not only would this limit be filled within minutes, but each user has to manually added. People have been asking for some kind of cloud support since I wrote JKSM on 3DS and this is my way to support it while getting around Google's restrictions.**
**Note: Due to Google's restrictions on unverified apps, you will be required to login once every seven days. There is nothing I can do about this at the moment.**
1. Go to https://console.cloud.google.com/cloud-resource-manager, if this is your first time, accept the terms and you should now have the dashboard in front of you.
2. Click `CREATE PROJECT` on the next screen.<br><center><img src="https://i.imgur.com/9SDS2e0.png" /></center>
3. On the next screen name your project JKSV. Organization is not required. Click create.
4. Give it a few seconds and the project should be created.
5. In the top left corner of your screen, click the navigation menu to the left of where it says **Google Cloud**. Find **Enabled APIs and services**. You may need to refresh the page if it doesn't update automatically to continue. <br><center><img src="https://i.imgur.com/JhqOpgc.png" /></center>
6. **Double check at this point to make 100% sure JKSV is the active project just in case it is not.** <br><center><img src="https://i.imgur.com/U49aIcb.png" /></center>
7. Once the dashboard loads, click **+ENABLE APIS AND SERVICES**. <br><center><img src="https://i.imgur.com/qaIhjID.png" /></center>
8. Scroll down a little and find Google Drive API under Google Workspace.<br><center><img src="https://i.imgur.com/cAC7h1r.png" /></center>
9. Click on it and click Enable on the next screen.
10. On the next screen, Google should be informing you that you need to create credentials in order to use Drive. Click Create Credentials.<br><center><img src="https://i.imgur.com/CRhFXQ4.png" /></center>
11. Under **Which API are you using?**, find **Cloud Storage API**. Under **What data will you be accessing?**, select **User data**. Click **Next**. <br><center><img src="https://i.imgur.com/fiulRpn.png" /></center>
12. Fill out the following screen. **Save and continue**.
13. Click **Add or Remove Scopes**.
14. Find **.../auth/drive** in the API list, select it, and click update. **Save and Continue**.
15. At this point you should be at a section named **OAuth Client ID**. Select **Desktop app**, name it **JKSV** and click **Create**.
16. Download the credentials saved in JSON format for later. Click **Done**.
17. Next, open the navigation menu in the top left again. Go down to **APIs and Services** and click on **OAuth consent screen**.<br><center><img src="https://i.imgur.com/OrMtG1x.png" /></center>
18. Scroll down to the section named **Test users**. Add yourself as a test user. This concludes the Google side of things.<br><center><img src="https://i.imgur.com/RTV2LMZ.png" /></center>
19. Next, find the JSON file you downloaded earlier. Copy it or send it via FTP to the following folder on your SD Card: `SD:/config/JKSV/`
20. The next time you start JKSV on your Switch, you should be prompted to login to Google via the Switch's web browser. Ta-da!
## <a name="webdav"></a><center> How to use WebDav with JKSV </center>
**NOTE: If you have [GDrive](#gdrive) configured (via JSON), it takes preference over WebDav**
1. Create a file `webdav.json` with the following content:
```json
{
"origin": "https://your-webdav-server",
"basepath": "optional-base-path",
"username": "testuser",
"password": "testpassword"
}
```
- `origin` (mandatory): protocol + serveraddress + (optional port), e.g. `https://your-webdav-server` or `http://localhost:8080` - **No trailing slash**
- `basepath` (optional): e.g. `dir`, `dir/subdir` must exist beforehand - **No leading AND trailing slash** - if your path uses special characters or spaces, they must be on URI encoding, by example `Game Saves` should be stored as `Game%20Saves`.
- `username` (optional): username, if server uses credentials
- `password` (optional): username, if server uses credentials
2. Copy file to following folder on your card `SD:/config/JKSV/`
3. The next time you start JKSV on your Switch, you should get a popup about the Webdav status
4. If problems arise, check the log at `SD:/JKSV/log.txt`
## Remote Changelog
- **07.27.2024**: **Breaking Change**. "Unsafe" characters were removed from titlename on the remote directory. That means,
that if you had existing safes under a title with unsafe characters, you will need to move them manually.

BIN
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
iconAssets/JKSV_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

1
iconAssets/JKSV_icon.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 967 1015"><defs><style>.cls-1{fill:#1c75bc;}.cls-2{fill:#fff;}.cls-3{fill:#231f20;}.cls-4{fill:#ed1c24;}</style></defs><path class="cls-1" d="M63.48,117.71V849.34c30.7,55.52,77.86,104.8,145.94,146.14H525.48v-961H135C92.47,41.32,69.69,70.26,63.48,117.71Z" transform="translate(-63.48 -34.48)"/><path class="cls-2" d="M525.48,564.48H264.66c-20.89,0-43.18-18.86-43.18-43.29V129.84c0-31.22,22.9-45.36,45.26-45.36H525.48Z" transform="translate(-63.48 -34.48)"/><path class="cls-3" d="M221.48,484.48v36.86c0,24.34,22.29,43.14,43.18,43.14H525.48v-80Z" transform="translate(-63.48 -34.48)"/><path class="cls-2" d="M221.48,642.52V914.43a24,24,0,0,0,24,24.05h280v-320h-280A24,24,0,0,0,221.48,642.52Zm213,270h-133v-266h133Z" transform="translate(-63.48 -34.48)"/><path class="cls-4" d="M778,84.48H622.48v965H778c139.44,0,252.49-113.05,252.49-252.49V337C1030.48,197.52,917.43,84.48,778,84.48ZM815.76,711.9a96.66,96.66,0,1,1,96.66-96.66A96.66,96.66,0,0,1,815.76,711.9Z" transform="translate(-63.48 -34.48)"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

40
inc/cfg.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
namespace cfg
{
typedef enum
{
ALPHA,
MOST_PLAYED,
LAST_PLAYED
} sortTypes;
void resetConfig();
void loadConfig();
void saveConfig();
bool isBlacklisted(uint64_t tid);
void addTitleToBlacklist(void *a);
void removeTitleFromBlacklist(uint64_t tid);
bool isFavorite(uint64_t tid);
void addTitleToFavorites(uint64_t tid);
bool isDefined(uint64_t tid);
void pathDefAdd(uint64_t tid, const std::string &newPath);
std::string getPathDefinition(uint64_t tid);
void addPathToFilter(uint64_t tid, const std::string &_p);
extern std::unordered_map<std::string, bool> config;
extern std::vector<uint64_t> blacklist;
extern std::vector<uint64_t> favorites;
extern uint8_t sortType;
extern std::string driveClientID, driveClientSecret, driveRefreshToken;
extern std::string webdavOrigin, webdavBasePath, webdavUser, webdavPassword;
} // namespace cfg

View File

@ -1,17 +0,0 @@
#ifndef CLSUI_H
#define CLSUI_H
#include "data.h"
namespace ui
{
void clsUserPrep();
void clsTitlePrep(data::user& u);
void classicUserMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p);
void classicTitleMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p);
//I don't think this matched very well
void classicFolderMenuUpdate(const uint64_t& down, const uint64_t& held, const touchPosition& p);
}
#endif // CLSUI_H

35
inc/curlfuncs.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <vector>
#define HEADER_ERROR "ERROR"
namespace curlFuncs
{
typedef struct
{
FILE *f;
uint64_t *o;
} curlUpArgs;
typedef struct
{
std::string path;
unsigned int size;
uint64_t *o;
} curlDlArgs;
size_t writeDataString(const char *buff, size_t sz, size_t cnt, void *u);
size_t writeHeaders(const char *buff, size_t sz, size_t cnt, void *u);
size_t readDataFile(char *buff, size_t sz, size_t cnt, void *u);
size_t readDataBuffer(char *buff, size_t sz, size_t cnt, void *u);
size_t writeDataFile(const char *buff, size_t sz, size_t cnt, void *u);
size_t writeDataBuffer(const char *buff, size_t sz, size_t cnt, void *u);
std::string getHeader(const std::string& _name, std::vector<std::string> *h);
//Shortcuts/legacy
std::string getJSONURL(std::vector<std::string> *headers, const std::string& url);
bool getBinURL(std::vector<uint8_t> *out, const std::string& url);
}

View File

@ -1,107 +1,131 @@
#ifndef DATA_H
#define DATA_H
#pragma once
#include <switch.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <vector>
#include "gfx.h"
#define BLD_MON 5
#define BLD_DAY 28
#define BLD_YEAR 2025
namespace data
{
extern bool sysSave;
//Loads user + title info
void loadDataInfo();
// Loads user + title info
void init();
void exit();
bool loadUsersTitles(bool clearUsers);
void sortUserTitles();
//Class to help not load the same icons over and over
class icn
// Draws some stats to the upper left corner
void dispStats();
// Global stuff for all titles/saves
typedef struct
{
public:
//Loads jpeg icon from jpegData
void load(const uint64_t& _id, const uint8_t *jpegData, const size_t& jpegSize);
//For loading default icon
void load(const uint64_t & _id, const std::string& _png);
/// @brief Control data.
NsApplicationControlData data;
void draw(unsigned x, unsigned y) { texDrawNoAlpha(iconTex, frameBuffer, x, y); }
void drawHalf(unsigned x, unsigned y) { texDrawSkipNoAlpha(iconTex, frameBuffer, x, y); }
/// @brief Saves whether or not the title has valid control data.
bool hasControlData = false;
uint64_t getTitleID() { return titleID; }
std::string title, safeTitle, author;
SDL_Texture *icon = NULL;
bool fav;
} titleInfo;
void deleteData() { texDestroy(iconTex); }
private:
uint64_t titleID;
tex *iconTex;
};
//Class to store title info
class titledata
// Holds stuff specific to user's titles/saves
typedef struct
{
public:
//Attempts to read title's info. Returns false if failed
bool init(const FsSaveDataInfo& inf);
// Makes it easier to grab id
uint64_t tid;
FsSaveDataInfo saveInfo;
PdmPlayStatistics playStats;
} userTitleInfo;
//Attempts to mount data with uID + id. Returns false if fails. For filtering.
bool isMountable(const u128& uID);
//Returns title + title without forbidden chars
std::string getTitle() { return title;}
std::string getTitleSafe() { return titleSafe; }
//Just for testing to make sure only ASCII folders
void debugCreate(const uint64_t& _id, const std::string& t);
//Returns ID
uint64_t getID() { return id; }
//Game icon
icn icon;
FsSaveDataType getType() { return type; }
private:
FsSaveDataType type;
std::string title, titleSafe;
uint64_t id;
u128 uID;
};
//Class to store user info + titles
// Class to store user info + titles
class user
{
public:
//Attempts to read user data using _id
bool init(const u128& _id);
user() = default;
user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName);
user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName, SDL_Texture *img);
//Allows user to init without reading data. For fun.
bool initNoChk(const u128& _id);
// Sets ID
void setUID(AccountUid _id);
//Returns user ID
u128 getUID() { return userID; }
// Assigns icon
void assignIcon(SDL_Texture *_icn)
{
userIcon = _icn;
}
//Returns username
std::string getUsername() { return username; }
std::string getUsernameSafe() { return userSafe; }
// Returns user ID
AccountUid getUID() const
{
return userID;
}
u128 getUID128() const
{
return uID128;
}
//Vector for storing save data info for user
std::vector<titledata> titles;
// Returns username
std::string getUsername() const
{
return username;
}
std::string getUsernameSafe() const
{
return userSafe;
}
tex *userIcon;
SDL_Texture *getUserIcon()
{
return userIcon;
}
void delIcon()
{
SDL_DestroyTexture(userIcon);
}
std::vector<data::userTitleInfo> titleInfo;
void addUserTitleInfo(const uint64_t &_tid,
const FsSaveDataInfo *_saveInfo,
const PdmPlayStatistics *_stats);
private:
u128 userID;
AccountUid userID;
u128 uID128;
std::string username, userSafe;
// User icon
SDL_Texture *userIcon;
};
//User vector
// User vector
extern std::vector<user> users;
// Title data/info map
extern std::unordered_map<uint64_t, data::titleInfo> titles;
//Stores current data we're using so I don't have to type so much.
extern titledata curData;
extern user curUser;
}
// Sets/Retrieves current user/title
void setUserIndex(unsigned _sUser);
data::user *getCurrentUser();
unsigned getCurrentUserIndex();
#endif // DATA_H
void setTitleIndex(unsigned _sTitle);
data::userTitleInfo *getCurrentUserTitleInfo();
unsigned getCurrentUserTitleInfoIndex();
// Gets pointer to info that also has title + nacp
data::titleInfo *getTitleInfoByTID(const uint64_t &tid);
// More shortcut functions
std::string getTitleNameByTID(const uint64_t &tid);
std::string getTitleSafeNameByTID(const uint64_t &tid);
SDL_Texture *getTitleIconByTID(const uint64_t &tid);
int getTitleIndexInUser(const data::user &u, const uint64_t &tid);
extern SetLanguage sysLang;
} // namespace data

View File

@ -1,61 +0,0 @@
#ifndef FILE_H
#define FILE_H
#include <string>
#include <vector>
#include <switch.h>
#include <dirent.h>
#include "data.h"
namespace fs
{
void init();
//Mounts usr's save data for open. Returns false it fails
bool mountSave(data::user& usr, data::titledata& open);
void copyFile(const std::string& from, const std::string& to);
void copyFileCommit(const std::string& from, const std::string& to, const std::string& dev);
//Recursively copies 'from' to 'to'
void copyDirToDir(const std::string& from, const std::string& to);
//Same as above, but commits data to 'dev' after every file is closed
void copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev);
//Recursively deletes 'path'
void delDir(const std::string& path);
//Dumps all titles for 'user'
void dumpAllUserSaves(data::user& u);
//returns file properties as C++ string
std::string getFileProps(const std::string& _path);
bool fileExists(const std::string& _path);
//Retrieves working dir string
std::string getWorkDir();
//Just retrieves a listing for _path and stores it in item vector
class dirList
{
public:
dirList(const std::string& _path);
void reassign(const std::string& _path);
void rescan();
std::string getItem(int index);
bool isDir(int index);
unsigned getCount();
private:
DIR *d;
struct dirent *ent;
std::string path;
std::vector<std::string> item;
};
}
#endif // FILE_H

65
inc/fs.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include <minizip/unzip.h>
#include <minizip/zip.h>
#include "fs/dir.h"
#include "fs/file.h"
#include "fs/fsfile.h"
#include "fs/fstype.h"
#include "fs/remote.h"
#include "fs/zip.h"
#include "ui/miscui.h"
#define BUFF_SIZE 0x4000
#define ZIP_BUFF_SIZE 0x20000
#define TRANSFER_BUFFER_LIMIT 0xC00000
namespace fs
{
copyArgs *copyArgsCreate(const std::string &src,
const std::string &dst,
const std::string &dev,
zipFile z,
unzFile unz,
bool _cleanup,
bool _trimZipPath,
uint8_t _trimPlaces);
void copyArgsDestroy(copyArgs *c);
void init();
bool mountSave(const FsSaveDataInfo &_m);
inline bool unmountSave()
{
return fsdevUnmountDevice("sv") == 0;
}
bool commitToDevice(const std::string &dev);
std::string getWorkDir();
void setWorkDir(const std::string &_w);
//Loads paths to filter from backup/deletion
void loadPathFilters(uint64_t tid);
bool pathIsFiltered(const std::string &_path);
void freePathFilters();
void createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t);
void createSaveDataThreaded(FsSaveDataType _type, uint64_t _tid, AccountUid _uid);
bool extendSaveData(const data::userTitleInfo *tinfo, uint64_t extSize, threadInfo *t);
void extendSaveDataThreaded(const data::userTitleInfo *tinfo, uint64_t extSize);
uint64_t getJournalSize(const data::userTitleInfo *tinfo);
uint64_t getJournalSizeMax(const data::userTitleInfo *tinfo);
//Always threaded
void wipeSave();
void createNewBackup(void *a);
void overwriteBackup(void *a);
void restoreBackup(void *a);
void deleteBackup(void *a);
void dumpAllUserSaves(void *a);
void dumpAllUsersAllSaves(void *a);
void logOpen();
void logWrite(const char *fmt, ...);
} // namespace fs

83
inc/fs/dir.h Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include "type.h"
#include <string>
#include <vector>
namespace fs
{
void mkDir(const std::string &_p);
void mkDirRec(const std::string &_p);
void delDir(const std::string &_p);
bool dirNotEmpty(const std::string &_dir);
bool isDir(const std::string &_path);
//threadInfo is optional. Only for updating task status.
void copyDirToDir(const std::string &src, const std::string &dst, threadInfo *t);
void copyDirToDirThreaded(const std::string &src, const std::string &dst);
void copyDirToDirCommit(const std::string &src, const std::string &dst, const std::string &dev, threadInfo *t);
void copyDirToDirCommitThreaded(const std::string &src, const std::string &dst, const std::string &dev);
void getDirProps(const std::string &path, unsigned &dirCount, unsigned &fileCount, uint64_t &totalSize);
class dirItem
{
public:
dirItem(const std::string &pathTo, const std::string &sItem);
std::string getItm() const
{
return itm;
}
std::string getName() const;
std::string getExt() const;
bool isDir() const
{
return dir;
}
private:
std::string itm;
bool dir = false;
};
//Just retrieves a listing for _path and stores it in item vector
class dirList
{
public:
dirList() = default;
dirList(const std::string &_path, bool ignoreDotFiles = false);
void reassign(const std::string &_path);
void rescan();
std::string getItem(int index) const
{
return item[index].getItm();
}
std::string getItemExt(int index) const
{
return item[index].getExt();
}
bool isDir(int index) const
{
return item[index].isDir();
}
unsigned getCount() const
{
return item.size();
}
fs::dirItem *getDirItemAt(unsigned int _ind)
{
return &item[_ind];
}
private:
std::string path;
std::vector<dirItem> item;
};
} // namespace fs

65
inc/fs/file.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include <string>
#include <cstdio>
#include <vector>
#include <switch.h>
#include <dirent.h>
#include <minizip/zip.h>
#include <minizip/unzip.h>
#include "fs.h"
#include "data.h"
#include "ui.h"
namespace fs
{
//Copy args are optional and only used if passed and threaded
void copyFile(const std::string& src, const std::string& dst, threadInfo *t);
void copyFileThreaded(const std::string& src, const std::string& dst);
void copyFileCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t);
void copyFileCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev);
void fileDrawFunc(void *a);
//deletes file
void delfile(const std::string& _p);
//Dumps all titles for current user
void dumpAllUserSaves();
void getShowFileProps(const std::string& _path);
void getShowDirProps(const std::string& _path);
bool fileExists(const std::string& _path);
//Returns file size
size_t fsize(const std::string& _f);
class dataFile
{
public:
dataFile(const std::string& _path);
~dataFile();
void close(){ fclose(f); }
bool isOpen() const { return opened; }
bool readNextLine(bool proc);
//Finds where variable name ends. When a '(' or '=' is hit. Strips spaces
void procLine();
std::string getLine() const { return line; }
std::string getName() const { return name; }
//Reads until ';', ',', or '\n' is hit and returns as string.
std::string getNextValueStr();
int getNextValueInt();
private:
FILE *f;
std::string line, name;
size_t lPos = 0;
bool opened = false;
};
void logOpen();
void logWrite(const char *fmt, ...);
void logClose();
}

100
inc/fs/fsfile.h Normal file
View File

@ -0,0 +1,100 @@
#pragma once
#include <switch.h>
#include <stdint.h>
//Bare minimum wrapper around switch fs for JKSV
#define FS_SEEK_SET 0
#define FS_SEEK_CUR 1
#define FS_SEEK_END 2
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct
{
FsFile _f;
Result error;
s64 offset, fsize;
} FSFILE;
int fsremove(const char *_p);
Result fsDelDirRec(const char *_p);
char *getDeviceFromPath(char *dev, size_t _max, const char *path);
char *getFilePath(char *pathOut, size_t _max, const char *path);
bool fsMkDir(const char *_p);
/*Opens file. Device is fetched from path. Libnx romfs doesn't work with this.
Mode needs to be:
FsOpenMode_Read
FsOpenMode_Write
FsOpenMode_Append
*/
bool fsfcreate(const char *_p, int64_t crSize);
FSFILE *fsfopen(const char *_p, uint32_t mode);
/*Same as above, but FsFileSystem _s is used. Path cannot have device in it*/
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode);
//Closes _f
inline void fsfclose(FSFILE *_f)
{
if(_f != NULL)
{
fsFileClose(&_f->_f);
free(_f);
}
}
//Seeks like stdio
inline void fsfseek(FSFILE *_f, int offset, int origin)
{
switch(origin)
{
case FS_SEEK_SET:
_f->offset = offset;
break;
case FS_SEEK_CUR:
_f->offset += offset;
break;
case FS_SEEK_END:
_f->offset = offset + _f->fsize;
break;
}
}
//Returns offset
inline size_t fsftell(FSFILE *_f) { return _f->offset; }
//Writes buf to file. Automatically resizes _f to fit buf
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f);
//Reads to buff
inline size_t fsfread(void *buf, size_t sz, size_t count, FSFILE *_f)
{
uint64_t read = 0;
_f->error = fsFileRead(&_f->_f, _f->offset, buf, sz * count, 0, &read);
_f->offset += read;
return read;
}
//Gets byte from file
inline char fsfgetc(FSFILE *_f)
{
char ret = 0;
uint64_t read = 0;
_f->error = fsFileRead(&_f->_f, _f->offset++, &ret, 1, 0, &read);
return ret;
}
//Writes byte to file
inline void fsfputc(int ch, FSFILE *_f) { fsfwrite(&ch, 1, 1, _f); }
#ifdef __cplusplus
}
#endif

46
inc/fs/fstype.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "data.h"
#include "miscui.h"
#include "type.h"
namespace fs
{
typedef struct
{
std::string src, dst, dev;
zipFile z;
unzFile unz;
bool cleanup = false, trimZipPath = false;
uint8_t trimZipPlaces = 0;
uint64_t offset = 0;
ui::progBar *prog;
threadStatus *thrdStatus;
Mutex arglck = 0;
void argLock() { mutexLock(&arglck); }
void argUnlock() { mutexUnlock(&arglck); }
} copyArgs;
typedef struct
{
FsSaveDataType type;
uint64_t tid;
AccountUid account;
uint16_t index;
} svCreateArgs;
typedef struct
{
const data::userTitleInfo *tinfo;
uint64_t extSize;
} svExtendArgs;
typedef struct
{
std::string path;
bool origin = false;
unsigned dirCount = 0;
unsigned fileCount = 0;
uint64_t totalSize = 0;
} dirCountArgs;
}

21
inc/fs/remote.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "../rfs.h"
#define JKSV_DRIVE_FOLDER "JKSV"
namespace fs
{
extern rfs::IRemoteFS *rfs;
extern std::string rfsRootID;
void remoteInit();
void remoteExit();
// Google Drive
void driveInit();
std::string driveSignInGetAuthCode();
// Webdav
void webDavInit();
}

18
inc/fs/zip.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <minizip/zip.h>
#include <minizip/unzip.h>
#include "type.h"
namespace fs
{
//threadInfo is optional and only used when threaded versions are used
void copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int trimPlaces, threadInfo *t);
void copyDirToZipThreaded(const std::string& src, zipFile dst, bool trimPath, int trimPlaces);
void copyZipToDir(unzFile src, const std::string& dst, const std::string& dev, threadInfo *t);
void copyZipToDirThreaded(unzFile src, const std::string& dst, const std::string& dev);
uint64_t getZipTotalSize(unzFile unz);
bool zipNotEmpty(unzFile unz);
}

66
inc/gd.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include <curl/curl.h>
#include <json-c/json.h>
#include <string>
#include <vector>
#include <unordered_map>
#include "curlfuncs.h"
#include "rfs.h"
#define HEADER_CONTENT_TYPE_APP_JSON "Content-Type: application/json; charset=UTF-8"
#define HEADER_AUTHORIZATION "Authorization: Bearer "
#define MIMETYPE_FOLDER "application/vnd.google-apps.folder"
namespace drive
{
class gd : public rfs::IRemoteFS
{
public:
void setClientID(const std::string& _clientID) { clientID = _clientID; }
void setClientSecret(const std::string& _clientSecret) { secretID = _clientSecret; }
void setRefreshToken(const std::string& _refreshToken) { rToken = _refreshToken; }
bool exhangeAuthCode(const std::string& _authCode);
bool hasToken() { return token.empty() == false; }
bool refreshToken();
bool tokenIsValid();
void clearDriveList() { driveList.clear(); }
// TODO: This also gets files that do not belong to JKSV
void driveListInit(const std::string& _q);
void driveListAppend(const std::string& _q);
std::vector<rfs::RfsItem> getListWithParent(const std::string& _parent);
void debugWriteList();
bool createDir(const std::string& _dirName, const std::string& _parent);
// TODO: This is problematic, because multiple files could share the same name without a parent.
bool dirExists(const std::string& _dirName);
bool dirExists(const std::string& _dirName, const std::string& _parent);
bool fileExists(const std::string& _filename, const std::string& _parent);
void uploadFile(const std::string& _filename, const std::string& _parent, curlFuncs::curlUpArgs *_upload);
void updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload);
void downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download);
void deleteFile(const std::string& _fileID);
std::string getClientID() const { return clientID; }
std::string getClientSecret() const { return secretID; }
std::string getRefreshToken() const { return rToken; }
std::string getFileID(const std::string& _name, const std::string& _parent);
// TODO: This is problematic, because multiple files could share the same name without a parent.
std::string getDirID(const std::string& _name);
std::string getDirID(const std::string& _name, const std::string& _parent);
size_t getDriveListCount() const { return driveList.size(); }
rfs::RfsItem *getItemAt(unsigned int _ind) { return &driveList[_ind]; }
private:
std::vector<rfs::RfsItem> driveList;
std::string clientID, secretID, token, rToken;
};
}

169
inc/gfx.h
View File

@ -1,157 +1,26 @@
#ifndef GFX_H
#define GFX_H
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <SDL2/SDL.h>
#include "textureMgr.h"
#ifdef __cplusplus
extern "C"
namespace gfx
{
#endif
extern SDL_Renderer *render;
extern gfx::textureMgr *texMgr;
//Structs
typedef struct
{
uint8_t r, g, b, a;
} clr;
void init();
void exit();
void present();
typedef struct
{
FT_Library lib;
FT_Face face;
FT_Error libRet, faceRet;
//Loads to buffer for speed for TTF
uint8_t *fntData;
} font;
void drawTextf(SDL_Texture *target, int fontSize, int x, int y, const SDL_Color *c, const char *fmt, ...);
void drawTextfWrap(SDL_Texture *target, int fontSize, int x, int y, int maxWidth, const SDL_Color* c, const char *fmt, ...);
size_t getTextWidth(const char *str, int fontSize);
typedef struct
{
size_t size;
unsigned width, height;
uint32_t *data;
} tex;
//Inits needed graphics stuff
bool graphicsInit(int windowWidth, int windowHeight);
//Exits needed services
bool graphicsExit();
//Flush, swap buffers
void gfxHandleBuffs();
//Creates color from uint32_t
inline clr clrCreateU32(uint32_t color)
{
clr ret;
ret.a = color >> 24 & 0xFF;
ret.b = color >> 16 & 0xFF;
ret.g = color >> 8 & 0xFF;
ret.r = color & 0xFF;
return ret;
//Shortcuts for drawing
void texDraw(SDL_Texture *target, SDL_Texture *tex, int x, int y);
void texDrawStretch(SDL_Texture *target, SDL_Texture *tex, int x, int y, int w, int h);
void texDrawPart(SDL_Texture *target, SDL_Texture *tex, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY);
void drawLine(SDL_Texture *target, const SDL_Color *c, int x1, int y1, int x2, int y2);
void drawRect(SDL_Texture *target, const SDL_Color *c, int x, int y, int w, int h);
void clearTarget(SDL_Texture *target, const SDL_Color *clear);
}
//Sets clr to [r], [g], [b], [a]
inline clr clrCreateRGBA(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a)
{
clr ret;
ret.r = _r;
ret.g = _g;
ret.b = _b;
ret.a = _a;
return ret;
}
//Inverts color
inline void clrInvert(clr *c)
{
c->r = (0xFF - c->r);
c->g = (0xFF - c->g);
c->b = (0xFF - c->b);
}
//Returns uint32_t color
inline uint32_t clrGetColor(const clr c)
{
return (c.a << 24 | c.b << 16 | c.g << 8 | c.r);
}
//Draws text using f
void drawText(const char *str, tex *target, const font *f, int x, int y, int sz, clr c);
//Returns text width
size_t textGetWidth(const char *str, const font *f, int sz);
//Clears framebuffer to c
void clearBufferColor(const clr c);
//Draws rectangle at x, y with w, h
void drawRect(tex *target, int x, int y, int w, int h, const clr c);
/*
TEX BEGIN
*/
//Inits empty tex
tex *texCreate(int w, int h);
//Loads PNG from path
tex *texLoadPNGFile(const char *path);
//Loads JPEG from path
tex *texLoadJPEGFile(const char *path);
//Loads jpeg from memory
tex *texLoadJPEGMem(const uint8_t *jpegData, size_t jpegSize);
//Frees memory used by t
void texDestroy(tex *t);
//Clears tex completely with c
void texClearColor(tex *in, const clr c);
//Draws t at x, y
void texDraw(const tex *t, tex *target, int x, int y);
//Draws without alpha blending, faster
void texDrawNoAlpha(const tex *t, tex *target, int x, int y);
//Draws skipping every other pixel + row
void texDrawSkip(const tex *t, tex *target, int x, int y);
//Same as above, no alpha
void texDrawSkipNoAlpha(const tex *t, tex *target, int x, int y);
//Draw t inverted at x, y
void texDrawInvert(const tex *t, tex *target, int x, int y);
//Replaces old with newColor
void texSwapColors(tex *t, const clr old, const clr newColor);
//Scales tex * scale and writes to out. Can only multiply for now
void texScaleToTex(const tex *in, tex *out, int scale);
//Draws directly to Switch's framebuffer
void texDrawDirect(const tex *in, int x, int y);
//Loads and returns font with Switch shared font loaded
font *fontLoadSharedFont(PlSharedFontType fontType);
//Loads and returns TTF font
font *fontLoadTTF(const char *path);
//Frees font
void fontDestroy(font *f);
/*
TEX END
*/
//returns framebuffer tex pointer
extern tex *frameBuffer;
#ifdef __cplusplus
}
#endif
#endif // GFX_H

33
inc/gfx/textureMgr.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <vector>
#include <SDL2/SDL.h>
/*Texture manager class
Keeps pointers to ALL textures so it is not possible to forget to free them. Also keeps code a little easier to read. A little.*/
typedef enum
{
IMG_FMT_PNG,
IMG_FMT_JPG,
IMG_FMT_BMP
} imgTypes;
namespace gfx
{
class textureMgr
{
public:
textureMgr() = default;
~textureMgr();
void textureAdd(SDL_Texture *_tex);
SDL_Texture *textureCreate(int _w, int _h);
SDL_Texture *textureLoadFromFile(const char *_path);
SDL_Texture *textureLoadFromMem(imgTypes _type, const void *_dat, size_t _datSize);
void textureResize(SDL_Texture **_tex, int _w, int _h);
private:
std::vector<SDL_Texture *> textures;
};
}

View File

@ -1,61 +0,0 @@
#ifndef KB_H
#define KB_H
#include <string>
#include <vector>
#include "miscui.h"
namespace ui
{
//The way it should have been. Inheritance drives me insane sometimes, but they're basically the same thing.
class key : button
{
public:
key(const std::string& _txt, const char& _l, unsigned _x, unsigned _y, unsigned _w, unsigned _h);
//Returns char assigned to key
char getLet() { return let; }
//Updates displayed text
void updateText(const std::string& txt);
//toUpper
void toCaps();
//toLower
void toLower();
void draw() { button::draw();}
void update(const touchPosition& p) { button::update(p);}
int getEvent() { return button::getEvent(); }
int getX() { return button::getX(); }
int getY() { return button::getY(); }
int getW() { return w; }
int getH() { return h; }
private:
char let;
};
class keyboard
{
public:
//Builds keyboard
keyboard();
~keyboard();
void draw();
//returns string
std::string getString(const std::string& def);
private:
std::vector<key> keys;
std::string str;
int selKey = 0;
uint8_t clrSh = 0;
bool clrAdd = true;
};
}
#endif // KB_H

View File

@ -1,58 +0,0 @@
#ifndef MENU_H
#define MENU_H
#include <string>
#include <vector>
#include "gfx.h"
#include "miscui.h"
enum menuTouch
{
MENU_NOTHING,
MENU_DOUBLE_REL
};
namespace ui
{
class menu
{
public:
void setParams(const unsigned& _x, const unsigned& _y, const unsigned& _rW);
//Adds option
void addOpt(const std::string& add);
//Clears menu stuff
~menu();
//Handles controller input
void handleInput(const uint64_t& down, const uint64_t& held, const touchPosition& p);
//Returns selected option
int getSelected() { return selected; }
//Returns touch event from buttons
int getTouchEvent() { return retEvent; }
//Draws the menu at x and y. rectWidth is the width of the rectangle drawn under the selected
void draw(const clr& textClr);
//Clears and resets menu
void reset();
private:
//drawing x and y + rectangle width
unsigned x = 0, y = 0, rW = 0, rY = 0;
//Options vector
std::vector<std::string> opt;
//Selected + frame counting for auto-scroll
int selected = 0, fc = 0, start = 0, retEvent = MENU_NOTHING;
//How much we shift the color of the rectangle
uint8_t clrSh = 0;
bool clrAdd = true;
ui::touchTrack track;
std::vector<ui::button> optButtons;
};
}
#endif

View File

@ -1,91 +0,0 @@
#ifndef MISCUI_H
#define MISCUI_H
enum buttonEvents
{
BUTTON_NOTHING,
BUTTON_PRESSED,
BUTTON_RELEASED
};
enum trackEvents
{
TRACK_NOTHING,
TRACK_SWIPE_UP,
TRACK_SWIPE_DOWN,
TRACK_SWIPE_LEFT,
TRACK_SWIPE_RIGHT
};
//For smaller classes that aren't easy to get lost in and general functions
namespace ui
{
//Progress bar for showing loading. Mostly so people know it didn't freeze
class progBar
{
public:
//Constructor. _max is the maximum value
progBar(const unsigned& _max);
//Updates progress
void update(const unsigned& _prog);
//Draws with text at top
void draw(const std::string& text);
private:
float max, prog, width;
};
class button
{
public:
button(const std::string& _txt, unsigned _x, unsigned _y, unsigned _w, unsigned _h);
void update(const touchPosition& p);
bool isOver();
bool wasOver();
int getEvent() { return retEvent; }
void draw();
unsigned getX() { return x; }
unsigned getY() { return y; }
unsigned getTx() { return tx; }
unsigned getTy() { return ty; }
protected:
bool pressed = false, first = false;
int retEvent = BUTTON_NOTHING;
unsigned x, y, w, h;
unsigned tx, ty;
std::string text;
touchPosition prev, cur;
};
class touchTrack
{
public:
void update(const touchPosition& p);
int getEvent() { return retTrack; }
int getOriginX() { return originX; }
int getOriginY() { return originY; }
private:
touchPosition pos[5];
int retTrack = TRACK_NOTHING;
int curPos = 0, avX = 0, avY = 0;
int originX = 0, originY = 0;
};
//General use
void showMessage(const std::string& mess);
void showError(const std::string& mess, const Result& r);
bool confirm(const std::string& q);
bool confirmTransfer(const std::string& f, const std::string& t);
bool confirmDelete(const std::string& p);
void drawTextbox(int x, int y, int w, int h);
void drawTextboxInvert(int x, int y, int w, int h);
}
#endif // MISCUI_H

53
inc/rfs.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <string>
#include "curlfuncs.h"
#include <mutex>
#define UPLOAD_BUFFER_SIZE 0x8000
#define DOWNLOAD_BUFFER_SIZE 0xC00000
#define USER_AGENT "JKSV"
namespace rfs {
typedef struct
{
std::string name, id, parent;
bool isDir = false;
unsigned int size;
} RfsItem;
class IRemoteFS
{
public:
virtual ~IRemoteFS() {} // Virtual destructor to allow correct deletion through the base class pointer
virtual bool createDir(const std::string& _dirName, const std::string& _parent) = 0;
virtual bool dirExists(const std::string& _dirName, const std::string& _parent) = 0;
virtual bool fileExists(const std::string& _filename, const std::string& _parent) = 0;
virtual void uploadFile(const std::string& _filename, const std::string& _parent, curlFuncs::curlUpArgs *_upload) = 0;
virtual void updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload) = 0;
virtual void downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download) = 0;
virtual void deleteFile(const std::string& _fileID) = 0;
virtual std::string getFileID(const std::string& _name, const std::string& _parent) = 0;
virtual std::string getDirID(const std::string& _name, const std::string& _parent) = 0;
virtual std::vector<RfsItem> getListWithParent(const std::string& _parent) = 0;
};
// Shared multi-threading definitions
typedef struct
{
curlFuncs::curlDlArgs *cfa;
std::mutex dataLock;
std::condition_variable cond;
std::vector<uint8_t> sharedBuffer;
bool bufferFull = false;
unsigned int downloaded = 0;
} dlWriteThreadStruct;
extern std::vector<uint8_t> downloadBuffer;
void writeThread_t(void *a);
size_t writeDataBufferThreaded(uint8_t *buff, size_t sz, size_t cnt, void *u);
}

28
inc/type.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <switch.h>
//Misc stuff for new menu code
typedef void (*funcPtr)(void *);
class threadStatus
{
public:
void setStatus(const char *fmt, ...);
void getStatus(std::string& statusOut);
private:
Mutex statusLock = 0;
std::string status;
};
typedef struct
{
bool running = false, finished = false;
Thread thrd;
ThreadFunc thrdFunc;
void *argPtr = NULL;
funcPtr drawFunc = NULL;//Draw func is passed threadInfo pointer too
threadStatus *status;
} threadInfo;

108
inc/ui.h
View File

@ -1,6 +1,6 @@
#ifndef UI_H
#define UI_H
#pragma once
#include <switch.h>
#include <vector>
#include <string>
@ -8,65 +8,103 @@
#include "gfx.h"
//ui headers - split up to keep a bit more organized
#include "kb.h"
#include "menu.h"
#include "miscui.h"
#include "clsui.h"
#include "uiupdate.h"
#include "ui/miscui.h"
#include "ui/uistr.h"
#include "ui/ttlview.h"
#include "ui/thrdProc.h"
#include "ui/sldpanel.h"
#include "ui/usr.h"
#include "ui/ttl.h"
#include "ui/fld.h"
#include "ui/sett.h"
#include "ui/ext.h"
#include "ui/fm.h"
enum menuState
{
USR_SEL,
TTL_SEL,
FLD_SEL,
ADV_MDE,
CLS_USR,
CLS_TTL,
CLS_FLD
EX_MNU,
OPT_MNU,
FIL_MDE
};
namespace ui
{
//Classic mode/text menus
extern bool clsMode;
//Current menu/ui state
extern int mstate;
extern int mstate, prevState;
//Slide/animation scaling
extern float animScale;
//Loading glyph
extern const std::string loadGlyphArray[];
//pad data cause i don't know where else to put it
extern PadState pad;
extern HidTouchScreenState touchState;
static inline void updateInput() { touchState = {0}; padUpdate(&pad); hidGetTouchScreenStates(&touchState, 1); }
inline uint64_t padKeysDown() { return padGetButtonsDown(&pad); }
inline uint64_t padKeysHeld() { return padGetButtons(&pad); }
inline uint64_t padKeysUp() { return padGetButtonsUp(&pad); }
inline void changeState(int newState)
{
prevState = mstate;
mstate = newState;
}
//Holds theme set id
extern ColorSetId thmID;
//Both UI modes need access to this
extern std::string folderMenuInfo;
//Colors to use now that I added theme detection
extern clr clearClr, mnuTxt, txtClr, rectLt, rectSh, tboxClr, sideRect;
/*Colors
clearClr = color to clear buffer
txtCont = text that contrasts clearClr
txtDiag = text color for dialogs
*/
extern SDL_Color clearClr, transparent, txtCont, txtDiag, rectLt, rectSh, tboxClr, sideRect, divClr, heartColor, slidePanelColor;
//Button tex
extern tex *buttonA, *buttonB, *buttonX, *buttonY, *buttonMin;
//Textbox graphics
extern tex *cornerTopLeft, *cornerTopRight, *cornerBottomLeft, *cornerBottomRight,\
*horEdgeTop, *horEdgeBot, *vertEdgeLeft, *vertEdgeRight;
//Selection box
extern tex *selBox;
extern SDL_Texture *cornerTopLeft, *cornerTopRight, *cornerBottomLeft, *cornerBottomRight;
//Menu bounding
extern SDL_Texture *mnuTopLeft, *mnuTopRight, *mnuBotLeft, *mnuBotRight;
//Shared font
extern font *shared;
//Covers left and right of progress bar to fake being not a rectangle.
extern SDL_Texture *progCovLeft, *progCovRight, *diaBox;
extern std::vector<ui::button> selButtons;
//Side bar from Freebird. RIP.
extern SDL_Texture *sideBar;
//Loads in the A, B, X, Y button graphics
//Sets colors and loads font for icon creation
void initTheme();
//Loads graphics and stuff
void init();
void exit();
//Prepares ui
//Sets up buttons for icon touchin
void setupSelButtons();
void setupNavButtons();
//Inits HID
void hidInit();
//Adds a panel pointer to a vector since they need to be drawn over everything else
int registerMenu(ui::menu *m);
int registerPanel(ui::slideOutPanel *sop);
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
//Just draws a screen and flips JIC boot takes long.
void showLoadScreen();
//Clears and draws general stuff used by multiple screens
void drawUI();
//switch case so we don't have problems with multiple main loops like 3DS
void runApp(const uint64_t& down, const uint64_t& held, const touchPosition& p);
}
bool runApp();
#endif
void showPopMessage(int frameCount, const char *fmt, ...);
//Used for multiple menu functions/callback
void toTTL(void *);
}

10
inc/ui/ext.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace ui
{
extern ui::menu *extMenu;
void extInit();
void extExit();
void extUpdate();
void extDraw(SDL_Texture *target);
}

19
inc/ui/fld.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "ui.h"
#include "dir.h"
namespace ui
{
//extern ui::menu *fldMenu;
extern ui::slideOutPanel *fldPanel;
//extern fs::dirList *fldList;
void fldInit();
void fldExit();
void fldUpdate();
//Populate to open menu, refresh for updating after actions
void fldPopulateMenu();
void fldRefreshMenu();
}

10
inc/ui/fm.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace ui
{
void fmInit();
void fmExit();
void fmPrep(FsSaveDataType _type, const std::string &_dev, const std::string &_baseSDMC, bool _commit);
void fmUpdate();
void fmDraw(SDL_Texture *target);
} // namespace ui

196
inc/ui/miscui.h Normal file
View File

@ -0,0 +1,196 @@
#pragma once
#include <SDL2/SDL.h>
#include <vector>
#include "gfx.h"
#include "type.h"
#define POP_FRAME_DEFAULT 130
#define MENU_FONT_SIZE_DEFAULT 18
#define MENU_MAX_SCROLL_DEFAULT 15
typedef enum
{
MENU_X,
MENU_Y,
MENU_RECT_WIDTH,
MENU_RECT_HEIGHT,
MENU_FONT_SIZE,
MENU_MAX_SCROLL
} menuParams;
//For smaller classes that aren't easy to get lost in and general functions
namespace ui
{
typedef struct
{
std::string text;
bool hold;
funcPtr confFunc, cancelFunc;
void *args;
unsigned lgFrame = 0, frameCount = 0; //To count frames cause I don't have time and am lazy
} confirmArgs;
typedef struct
{
funcPtr func = NULL;
void *args = NULL;
HidNpadButton button = (HidNpadButton)0;
} menuOptEvent;
typedef struct
{
SDL_Texture *icn = NULL;
std::string txt;
int txtWidth;
std::vector<menuOptEvent> events;
} menuOpt;
class menu
{
public:
menu() = default;
menu(int _x, int _y, int _rW, int _fS, int _mL);
void editParam(int _param, int newVal);
//Gets executed when menu changes at all
void setOnChangeFunc(funcPtr func)
{
onChange = func;
}
//executed when .update() is called.
void setCallback(funcPtr _callback, void *args)
{
callback = _callback;
callbackArgs = args;
}
//Adds option.
int addOpt(SDL_Texture *_icn, const std::string &add);
//Adds an function to be executed on pressing button specified
void optAddButtonEvent(unsigned _ind, HidNpadButton _button, funcPtr _func, void *args);
//Changes opt text
void editOpt(int ind, SDL_Texture *_icn, const std::string &ch);
size_t getOptCount()
{
return opt.size();
}
int getOptPos(const std::string &txt);
//Clears menu stuff
~menu();
//Handles controller input and executes functions for buttons if they're set
void update();
//Returns selected option
int getSelected()
{
return selected;
}
//Returns menu option count
int getCount()
{
return opt.size();
}
//Draws the menu at x and y. rectWidth is the width of the rectangle drawn under the selected
void draw(SDL_Texture *target, const SDL_Color *textClr, bool drawText);
//Clears and resets menu
void reset();
//Resets selected + start
void resetSel()
{
selected = 0;
}
//Enables control/disables drawing select box
void setActive(bool _set);
bool getActive()
{
return isActive;
}
private:
//drawing x and y + rectangle width/height. Height is calc'd with font size
int x = 0, mY = 0, tY = 0, y = 0, rW = 0, rY = 0, fSize = 0, rH = 0, mL = 0;
//Options vector
std::vector<ui::menuOpt> opt;
//Selected + frame counting for auto-scroll. Hover count is to not break autoscroll
int selected = 0, fc = 0, hoverCount = 0, spcWidth = 0;
//How much we shift the color of the rectangle
uint8_t clrSh = 0;
bool clrAdd = true, isActive = true, hover = false;
//Option buffer. Basically, text is draw to this so it can't overlap. Also allows scrolling
SDL_Texture *optTex;
funcPtr onChange = NULL, callback = NULL;
void *callbackArgs, *funcArgs;
};
//Progress bar for showing loading. Mostly so people know it didn't freeze
class progBar
{
public:
//Constructor. _max is the maximum value
progBar() = default;
progBar(uint64_t _max) : max(_max) {};
void setMax(uint64_t _max)
{
max = _max;
}
//Updates progress
void update(uint64_t _prog);
//Draws with text at top
void draw(const std::string &text);
private:
uint64_t max = 0, prog = 0;
float width = 0;
};
typedef struct
{
std::string message;
int rectWidth = 0, frames = 0, y = 720;
} popMessage;
class popMessageMngr
{
public:
~popMessageMngr();
void update();
void popMessageAdd(const std::string &mess, int frameTime);
void draw();
private:
std::vector<popMessage>
popQueue; //All graphics need to be on main thread. Directly adding will cause text issues
std::vector<popMessage> message;
};
//General use
ui::confirmArgs *confirmArgsCreate(bool _hold,
funcPtr _confFunc,
funcPtr _cancelFunc,
void *_funcArgs,
const char *fmt,
...);
void confirm(void *a);
void showMessage(const char *fmt, ...);
bool confirmTransfer(const std::string &f, const std::string &t);
bool confirmDelete(const std::string &p);
void drawBoundBox(SDL_Texture *target, int x, int y, int w, int h, uint8_t clrSh);
void drawTextbox(SDL_Texture *target, int x, int y, int w, int h);
} // namespace ui

13
inc/ui/sett.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <SDL2/SDL.h>
namespace ui
{
void settInit();
void settExit();
void settUpdate();
void settDraw(SDL_Texture *target);
extern ui::menu *settMenu;
}

35
inc/ui/sldpanel.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
namespace ui
{
//Maybe more later *if* needed, but not now.
typedef enum
{
SLD_LEFT,
SLD_RIGHT
} slidePanelOrientation;
//_draw is called and passed the panel texture/target when this.draw() is called.
class slideOutPanel
{
public:
slideOutPanel(int _w, int _h, int _y, slidePanelOrientation _side, funcPtr _draw);
void resizePanel(int _w, int _h, int _y);
void update();
void setCallback(funcPtr _cb, void *_args) { callback = _cb; cbArgs = _args; }
void openPanel() { open = true; }
void closePanel() { open = false; }
void setX(int _nX){ x = _nX; };
bool isOpen() { return open; }
void draw(const SDL_Color *backCol);
private:
int w, h, x, y;
uint8_t sldSide;
bool open = false;
SDL_Texture *panel;
funcPtr drawFunc, callback = NULL;
void *cbArgs = NULL;
};
}

24
inc/ui/thrdProc.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "type.h"
namespace ui
{
class threadProcMngr
{
public:
~threadProcMngr();
//Draw function is used and called to draw on overlay
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
void update();
void draw();
bool empty() { return threads.empty(); }
private:
std::vector<threadInfo *> threads;
uint8_t lgFrame = 0, clrShft = 0;
bool clrAdd = true;
unsigned frameCount = 0;
Mutex threadLock = 0;
};
}

17
inc/ui/ttl.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
namespace ui
{
void ttlInit();
void ttlExit();
void ttlSetActive(int usr, bool _set, bool _showSel);
void ttlRefresh();
//JIC for func ptr
void ttlReset();
void ttlUpdate();
void ttlDraw(SDL_Texture *target);
//File mode needs access to this.
extern ui::slideOutPanel *ttlOptsPanel;
}

56
inc/ui/ttlview.h Normal file
View File

@ -0,0 +1,56 @@
#pragma once
#include <SDL2/SDL.h>
#include "type.h"
#include "data.h"
namespace ui
{
class titleTile
{
public:
titleTile(unsigned _w, unsigned _h, bool _fav, SDL_Texture *_icon)
{
w = _w;
h = _h;
wS = _w;
hS = _h;
fav = _fav;
icon = _icon;
}
void draw(SDL_Texture *target, int x, int y, bool sel);
private:
unsigned w, h, wS, hS;
bool fav = false;
SDL_Texture *icon;
};
//Todo less hardcode etc
class titleview
{
public:
titleview(const data::user& _u, int _iconW, int _iconH, int _horGap, int _vertGap, int _rowCount, funcPtr _callback);
~titleview();
void update();
void refresh();
void setActive(bool _set, bool _showSel) { active = _set; showSel = _showSel; }
bool getActive() { return active; }
void setSelected(int _set) { selected = _set; }
int getSelected() { return selected; }
void draw(SDL_Texture *target);
private:
const data::user *u;//Might not be safe. Users *shouldn't* be touched after initial load
bool active = false, showSel = false, clrAdd = true;
uint8_t clrShft = 0;
funcPtr callback = NULL;
int x = 200, y = 62, selected = 0, selRectX = 10, selRectY = 45;
int iconW, iconH, horGap, vertGap, rowCount;
std::vector<ui::titleTile *> tiles;
};
}

15
inc/ui/uistr.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <map>
//Strings since translation support
namespace ui
{
void initStrings();
void loadTrans();
void saveTranslationFiles(void *a);
extern std::map<std::pair<std::string, int>, std::string> strings;
inline std::string getUIString(const std::string& _name, int ind){ return strings[std::make_pair(_name, ind)]; }
inline const char *getUICString(const std::string& _name, int ind){ return strings[std::make_pair(_name, ind)].c_str(); }
}

14
inc/ui/usr.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
namespace ui
{
void usrInit();
void usrExit();
void usrRefresh();
void usrUpdate();
void usrDraw(SDL_Texture *target);
//A lot of stuff needs access to these
extern ui::menu *usrMenu;
extern ui::slideOutPanel *usrSelPanel;
}

View File

@ -1,19 +0,0 @@
#ifndef UIUPDATE_H
#define UIUPDATE_H
//Contains declarations of ui updating functions
namespace ui
{
void updateUserMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p);
void updateTitleMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p);
void updateFolderMenu(const uint64_t& down, const uint64_t& held, const touchPosition& p);
void updateAdvMode(const uint64_t& down, const uint64_t& held, const touchPosition& p);
//needed here since it uses static menu
void folderMenuPrepare(data::user& usr, data::titledata& dat);
void advCopyMenuPrep();
void advModePrep();
}
#endif // USRSEL_H

View File

@ -1,34 +1,161 @@
#ifndef UTIL_H
#define UTIL_H
#pragma once
#include "data.h"
#include "ui.h"
#include "file.h"
#include "gfx.h"
#include "ui.h"
namespace util
{
//Returns string with date + time
std::string getDateTime();
enum
{
DATE_FMT_YMD,
DATE_FMT_YDM,
DATE_FMT_HOYSTE,
DATE_FMT_JHK,
DATE_FMT_ASC
};
//Creates Dir 'JKSV/[title]
void makeTitleDir(data::user& u, data::titledata& t);
typedef enum
{
CPU_SPEED_204MHz = 204000000,
CPU_SPEED_306MHz = 306000000,
CPU_SPEED_408MHz = 408000000,
CPU_SPEED_510MHz = 510000000,
CPU_SPEED_612MHz = 612000000,
CPU_SPEED_714MHz = 714000000,
CPU_SPEED_816MHz = 816000000,
CPU_SPEED_918MHz = 918000000,
CPU_SPEED_1020MHz = 1020000000, //Default
CPU_SPEED_1122MHz = 1122000000,
CPU_SPEED_1224MHz = 1224000000,
CPU_SPEED_1326MHz = 1326000000,
CPU_SPEED_1428MHz = 1428000000,
CPU_SPEED_1581MHz = 1581000000,
CPU_SPEED_1683MHz = 1683000000,
CPU_SPEED_1785MHz = 1785000000
} cpuSpds;
//Returns 'JKSV/[title]/'
std::string getTitleDir(data::user& u, data::titledata& t);
typedef enum
{
GPU_SPEED_0MHz = 0,
GPU_SPEED_76MHz = 76800000,
GPU_SPEED_153MHz = 153600000,
GPU_SPEED_203MHz = 230400000,
GPU_SPEED_307MHz = 307200000, //Handheld 1
GPU_SPEED_384MHz = 384000000, //Handheld 2
GPU_SPEED_460MHz = 460800000,
GPU_SPEED_537MHz = 537600000,
GPU_SPEED_614MHz = 614400000,
GPU_SPEED_768MHz = 768000000, //Docked
GPU_SPEED_844MHz = 844800000,
GPU_SPEED_921MHZ = 921600000
} gpuSpds;
//Just returns string with '\n' inserted.
std::string getWrappedString(const std::string& s, const unsigned& sz, const unsigned& maxWidth);
typedef enum
{
RAM_SPEED_0MHz = 0,
RAM_SPEED_40MHz = 40800000,
RAM_SPEED_68MHz = 68000000,
RAM_SPEED_102MHz = 102000000,
RAM_SPEED_204MHz = 204000000,
RAM_SPEED_408MHz = 408000000,
RAM_SPEED_665MHz = 665600000,
RAM_SPEED_800MHz = 800000000,
RAM_SPEED_1065MHz = 1065600000,
RAM_SPEED_1331MHz = 1331200000,
RAM_SPEED_1600MHz = 1600000000
} ramSpds;
//Returns string with date S+ time
std::string getDateTime(int fmt);
//Copys dir list to a menu with 'D: ' + 'F: '
void copyDirListToMenu(fs::dirList& d, ui::menu& m);
void copyDirListToMenu(const fs::dirList &d, ui::menu &m);
//Removes last folder from '_path'
void removeLastFolderFromString(std::string& _path);
void removeLastFolderFromString(std::string &_path);
size_t getTotalPlacesInPath(const std::string &_path);
void trimPath(std::string &_path, uint8_t _places);
std::string safeString(const std::string& s);
inline bool isASCII(uint32_t t)
{
return t > 30 && t < 127;
}
std::string getInfoString(data::user& u, data::titledata& d);
std::string safeString(const std::string &s);
void debugPrintf(const char *out);
}
#endif // UTIL_H
std::string getStringInput(SwkbdType _type,
const std::string &def,
const std::string &head,
size_t maxLength,
unsigned dictCnt,
const std::string dictWords[]);
std::string getExtensionFromString(const std::string &get);
std::string getFilenameFromPath(const std::string &get);
std::string generateAbbrev(uint64_t tid);
//removes char from C++ string
void stripChar(char _c, std::string &_s);
void replaceStr(std::string &_str, const std::string &_find, const std::string &_rep);
//For future external translation support. Replaces [button] with button chars
void replaceButtonsInString(std::string &rep);
//Creates a basic generic icon for stuff without one
SDL_Texture *createIconGeneric(const char *txt, int fontSize, bool clearBack);
inline u128 accountUIDToU128(AccountUid uid)
{
return ((u128)uid.uid[0] << 64 | uid.uid[1]);
}
inline AccountUid u128ToAccountUID(u128 id)
{
AccountUid ret;
ret.uid[0] = id >> 64;
ret.uid[1] = id;
return ret;
}
inline std::string getIDStr(uint64_t _id)
{
char tmp[18];
sprintf(tmp, "%016lX", _id);
return std::string(tmp);
}
inline std::string getIDStrLower(uint64_t _id)
{
char tmp[18];
sprintf(tmp, "%08X", (uint32_t)_id);
return std::string(tmp);
}
inline std::string generatePathByTID(uint64_t tid)
{
return fs::getWorkDir() + data::getTitleSafeNameByTID(tid) + "/";
}
std::string getSizeString(uint64_t _size);
inline void createTitleDirectoryByTID(uint64_t tid)
{
std::string makePath = fs::getWorkDir() + data::getTitleSafeNameByTID(tid);
mkdir(makePath.c_str(), 777);
}
void sysBoost();
void sysNormal();
inline bool isApplet()
{
AppletType type = appletGetAppletType();
return type == AppletType_LibraryApplet;
}
void checkForUpdate(void *a);
} // namespace util

53
inc/webdav.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <curl/curl.h>
#include <string>
#include <tinyxml2.h>
#include "rfs.h"
namespace rfs {
// Note: Everything declared an "id" is the full path component from the origin to the resource starting with a "/".
// Note: Directories ALWAYS have a trailing / while files NEVER have a trailing /
// e.g. /<basePath>/JKSV/
// e.g. /<basePath>/JKSV/<title-id>/
// e.g. /<basePath>/JKSV/<title-id>/<file>
// e.g. /
// other string arguments never have any leading or trailing "/"
class WebDav : public IRemoteFS {
private:
CURL* curl;
std::string origin;
std::string basePath;
std::string username;
std::string password;
std::vector<RfsItem> parseXMLResponse(const std::string& xml);
bool resourceExists(const std::string& id);
std::string appendResourceToParentId(const std::string& resourceName, const std::string& parentId, bool isDir);
std::string getNamespacePrefix(tinyxml2::XMLElement* root, const std::string& nsURI);
public:
WebDav(const std::string& origin,
const std::string& username,
const std::string& password);
~WebDav();
bool createDir(const std::string& dirName, const std::string& parentId);
bool dirExists(const std::string& dirName, const std::string& parentId);
bool fileExists(const std::string& filename, const std::string& parentId);
void uploadFile(const std::string& filename, const std::string& parentId, curlFuncs::curlUpArgs *_upload);
void updateFile(const std::string& fileID, curlFuncs::curlUpArgs *_upload);
void downloadFile(const std::string& fileID, curlFuncs::curlDlArgs *_download);
void deleteFile(const std::string& fileID);
std::string getFileID(const std::string& name, const std::string& parentId);
std::string getDirID(const std::string& dirName, const std::string& parentId);
std::vector<RfsItem> getListWithParent(const std::string& _parent);
std::string getDisplayNameFromURL(const std::string &url);
};
}

BIN
romfs/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 693 B

BIN
romfs/img/fb/lDark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
romfs/img/fb/lLight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
romfs/img/icn/icnDrk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
romfs/img/icn/icnLght.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

BIN
romfs/img/icn/icon.msk Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

171
romfs/lang/de.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/en-GB.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/en-US.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

218
romfs/lang/es-419.txt Normal file
View File

@ -0,0 +1,218 @@
appletModeWarning = 0, "*ADVERTENCIA*: JKSV se está ejecutando en modo 'applet'. Algunas características no funcionarán."
author = 0, "Impeeza"
confirmBlacklist = 0, "¿Estás seguro que deseas adicionar #%s# a la lista negra?"
confirmCopy = 0, "¿Seguro que desea copiar #%s# a #%s#?"
confirmCreateAllSaveData = 0, "¿Seguro que desea crear todos los Datos de Partidas Guardadas en este sistema para #%s#? ¡Puede tomar un tiempo dependiendo de la cantidad de títulos sean encontrados!"
confirmDelete = 0, "¿Seguro que desea borrar #%s#? *¡Es permanente!*"
confirmDeleteBackupsAll = 0, "¿Seguro que desea borrar *TODOS* los respaldos de partidas guardadas para todos sus juegos?"
confirmDeleteBackupsTitle = 0, "¿Seguro que desea borrar *TODOS* los respaldos de partidas guardadas para #%s#?"
confirmDeleteSaveData = 0, "*ADVERTENCIA*: Ésto eliminará la partida guardada para #%s# *DE SU SISTEMA*. ¿Está seguro que así lo desea?"
#CHANGED=============================================>
confirmDriveOverwrite = 0, "Al descargar este respaldo desde el servidor remoto se reemplazará el que actualmente está en la tarjeta SD, ¿Continuar?"
#<====================================================
confirmOverwrite = 0, "¿Sobrescribir #%s#?"
confirmResetSaveData = 0, "*ADVERTENCIA*: Ésto restablecerá la partida guardada para este juego, como si nunca se hubiera ejecutado. ¿Está seguro que es lo que desea?"
confirmRestore = 0, "¿Seguro que desea restaurar #%s#?"
#CHANGED=============================================>
debugStatus = 0, "Número de Usuarios: "
debugStatus = 1, "Usuario Actual: "
debugStatus = 2, "Título Actual: "
debugStatus = 3, "Título Seguro: "
debugStatus = 4, "Tipo órden: "
#<====================================================
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Si [A]"
extrasMenu = 0, "Navegador de SD a SD"
extrasMenu = 1, "Navegar 'ProdInfo'"
extrasMenu = 2, "Navegar 'Safe'"
extrasMenu = 3, "Navegar 'System'"
extrasMenu = 4, "Navegar 'User'"
extrasMenu = 5, "Eliminar actualizaciones pendientes"
extrasMenu = 6, "Detener proceso"
extrasMenu = 7, "Montar 'System Save'"
extrasMenu = 8, "Recargar Lista de Títulos"
extrasMenu = 9, "Navegar 'Process RomFS'"
extrasMenu = 10, "Realizar respaldo de la carpeta de JKSV"
extrasMenu = 11, "*[DEV]* Extraer Archivos de Lenguaje actuales"
fileModeFileProperties = 0, "Ruta: %s\nTamaño: %s"
fileModeFolderProperties = 0, "Ruta: %s\nSubCarpetas: %u\nCantidad Archivos: %u\nTamaño Total: %s"
fileModeMenu = 0, "Copiar a "
fileModeMenu = 1, "Borrar"
fileModeMenu = 2, "Renombrar"
fileModeMenu = 3, "Crear Carpeta"
fileModeMenu = 4, "Propiedades"
fileModeMenu = 5, "Cerrar"
fileModeMenu = 6, "Adicionar a filtros de Carpetas"
fileModeMenuMkDir = 0, "Nuevo"
folderMenuNew = 0, "Nuevo Respaldo"
#CHANGED=============================================>
helpFolder = 0, "[A] Selecionar [Y] Restaurar [X] Borrar [ZR] Subir [B] Cerrar"
#<====================================================
helpSettings = 0, "[A] Cambiar [X] Predeterminados [B] Atrás"
helpTitle = 0, "[A] Seleccionar [L][R] Cambiar Página [Y] Favoritos [X] Opciones de Títulos [B] Atrás"
helpUser = 0, "[A] Seleccionar [Y] Extraer Todos [X] Opciones de Usuario"
holdingText = 0, "(Sostenga) "
holdingText = 1, "(Siga Sosteniendo) "
holdingText = 2, "(¡Ya Casi!) "
#CHANGED=============================================>
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Tiempo Jugado: %02d:%02d"
infoStatus = 3, "Veces Ejecutado: %u"
infoStatus = 4, "Editor: %s"
infoStatus = 5, "Tipo Partida Guardada: %s"
infoStatus = 6, "Índice de Caché: %u"
infoStatus = 7, "Usuario: %s"
infoStatus = 9, ""
#<=====================================================
loadingStartPage = 0, "Cargando..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Configs."
onlineErrorConnecting = 0, "¡Error Conectando!"
onlineNoUpdates = 0, "No hay actualizaciones disponibles."
popAddedToPathFilter = 0, "Se adicionó '#%s#' a filtros de carpeta."
popCPUBoostEnabled = 0, "Aumento de CPU para ZIP."
popChangeOutputError = 0, "#%s# contiene carácteres ilegales o no-ASCII."
popChangeOutputFolder = 0, "Se cambia #%s# por #%s#"
#CHANGED=============================================>
popDriveFailed = 0, "Falla al iniciar Google Drive."
popRemoteNotActive = 0, "Servidor Remoto no está disponible."
popDriveStarted = 0, "Se ha iniciado Google Drive correctamente."
popWebdavStarted = 0, "Se ha iniciado Webdav Correctamente."
popWebdavFailed = 0, "Falla al iniciar Webdav."
#<====================================================
popErrorCommittingFile = 0, "¡Error guardando archivo!"
popFolderIsEmpty = 0, "¡La carpeta está vacía!"
popProcessShutdown = 0, "#%s# Apagado satisfactorio."
#CHANGED=============================================>
popSVIExported = 0, "SVI Exportado."
#<====================================================
popSaveIsEmpty = 0, "¡Partida Guardada está vacía!"
popTrashEmptied = 0, "Papelera Vaciada"
popZipIsEmpty = 0, "¡Archivo ZIP está vacío!"
saveDataBackupDeleted = 0, "#%s# ha sido borrado."
saveDataBackupMovedToTrash = 0, "#%s# ha sido movido a papelera."
saveDataCreatedForUser = 0, "Se creó Partida Guardada para %s!"
saveDataCreationFailed = 0, "¡Error en creación de Partida Guardada!"
saveDataDeleteAllUser = 0, "*¿SEGURO QUE DESEA BORRAR TODAS LAS PARTIDAS GUARDADAS DE %s?*"
saveDataDeleteSuccess = 0, "¡Se borró la Partida Guardada de #%s#!"
saveDataExtendFailed = 0, "Error al expandir la partida guardada."
saveDataExtendSuccess = 0, "¡Partida Guardada para #%s# expandida con éxito!"
saveDataIndexText = 0, "Guardar Índice: "
saveDataNoneFound = 0, "¡No se encontraron partidas guardadas para #%s#!"
saveDataResetSuccess = 0, "¡Partida Guardada para #%s# restablecida!"
saveDataTypeText = 0, "Datos de Sistema"
saveDataTypeText = 1, "Partida Guardada"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Datos de Dispositivo"
saveDataTypeText = 4, "Almacenamiento Temporal"
saveDataTypeText = 5, "Almacenamiento de Caché"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Caché"
saveTypeMainMenu = 3, "Sistema"
saveTypeMainMenu = 4, "BCAT de Sistema"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Vaciar Papelera"
settingsMenu = 1, "Buscar Actualizaciones"
settingsMenu = 2, "Configurar carpeta de salida para JKSV"
settingsMenu = 3, "Editar lista negra de Títulos"
settingsMenu = 4, "Eliminar todos los respaldos"
settingsMenu = 5, "Incluír Datos de Dispositivo con los Usuarios: "
settingsMenu = 6, "Crear respaldo automático al restaurar: "
settingsMenu = 7, "Nombrar Automáticamente los respaldos: "
settingsMenu = 8, "Overclock/Aumento de CPU: "
settingsMenu = 9, "Sostener para Borrar: "
settingsMenu = 10, "Sostener para Restaurar: "
settingsMenu = 11, "Sostener para Sobreescribir: "
settingsMenu = 12, "Montar forzado: "
settingsMenu = 13, "Datos de la cuenta de Sistema: "
settingsMenu = 14, "Habilitar escritura para datos de Sistema: "
settingsMenu = 15, "Usar comandos FS directamente: "
settingsMenu = 16, "Exportar partidas guardadas a archivos ZIP: "
settingsMenu = 17, "Forzar el uso de idioma Inglés: "
settingsMenu = 18, "Habilitar papelera: "
settingsMenu = 19, "Ordenar Títulos: "
settingsMenu = 20, "Escala de Animación: "
settingsMenu = 21, "Subir automáticamente a GDrive/Webdav: "
settingsOff = 0, "Apagado"
settingsOn = 0, ">Encendido>"
sortType = 0, "Alfabéticamente"
sortType = 1, "Por Duración Juegos"
sortType = 2, "Recientemente Jugado"
swkbdEnterName = 0, "Ingrese un nuevo nombre"
swkbdExpandSize = 0, "Ingrese nuevo tamaño en MB"
swkbdMkDir = 0, "Ingrese nombre de carpeta"
swkbdNewSafeTitle = 0, "Ingrese Nueva Carpeta de Salida"
swkbdProcessID = 0, "Ingrese ID de proceso"
swkbdRename = 0, "Ingrese un nuevo nombre para el elemento"
swkbdSaveIndex = 0, "Ingrese Índice de Caché"
swkbdSetWorkDir = 0, "Ingrese Nueva Carpeta de Salida"
swkbdSysSavID = 0, "Ingrese ID de Datos de Sistema"
threadStatusAddingFileToZip = 0, "Agregando '#%s#' a archivo ZIP..."
#CHANGED=============================================>
threadStatusCalculatingSaveSize = 0, "Calculando tamaño de partida guardada..."
#<====================================================
threadStatusCheckingForUpdate = 0, "Verificando actualizaciones..."
#CHANGED=============================================>
threadStatusCompressingSaveForUpload = 0, "Comprimiendo #%s# para subirlo..."
#<====================================================
threadStatusCopyingFile = 0, "Copiando '#%s#'..."
threadStatusCreatingSaveData = 0, "Creando Partida Guardada para #%s#..."
threadStatusDecompressingFile = 0, "Descomprimiendo '#%s#'..."
threadStatusDeletingFile = 0, "Borrando..."
threadStatusDeletingSaveData = 0, "Borrando Partida Guardada para #%s#..."
threadStatusDeletingUpdate = 0, "Borrando Actualización Pendiente..."
#CHANGED=============================================>
threadStatusDownloadingFile = 0, "Descargando #%s#..."
#<====================================================
threadStatusDownloadingUpdate = 0, "Borrando Actualización..."
threadStatusExtendingSaveData = 0, "Expandiendo Datos Guardados para #%s#..."
threadStatusGetDirProps = 0, "Obteniendo propiedades de Carpeta..."
threadStatusOpeningFolder = 0, "Abriendo '#%s#'..."
threadStatusPackingJKSV = 0, "Guardando contenidos de carpeta de JKSV en archivo ZIP..."
threadStatusResettingSaveData = 0, "Restableciendo datos de partida..."
threadStatusSavingTranslations = 0, "Guardando archivo maestro..."
#CHANGED=============================================>
threadStatusUploadingFile = 0, "Subiendo #%s#..."
#<====================================================
titleOptions = 0, "Información"
titleOptions = 1, "Lista Negra"
titleOptions = 2, "Cambiar Carpeta de Salida"
titleOptions = 3, "Abrir en Modo Archivo"
titleOptions = 4, "Borrar todos los respaldos"
titleOptions = 5, "Restablecer Datos de Partida"
titleOptions = 6, "Borrar Datos de Partida"
titleOptions = 7, "Expandir Datos de Partida"
#CHANGED=============================================>
titleOptions = 8, "Exportar SVI"
#<====================================================
translationMainPage = 0, "Traducción: "
userOptions = 0, "Extrar Todo para "
userOptions = 1, "Crear Datos de Partida"
userOptions = 2, "Crear Todos los Datos de Partida"
userOptions = 3, "Borrar Datos de Partida para todos los Usuarios"

171
romfs/lang/es.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/fr-CA.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/fr.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "Shadow2560"
confirmBlacklist = 0, "Souhaitez-vous vraiment ajouter #%s# à votre liste noire?"
confirmCopy = 0, "Souhaitez-vous vraiment copier #%s# vers #%s#?"
confirmCreateAllSaveData = 0, "Souhaitez-vous vraiment créer toutes les données de sauvegarde de ce système pour #%s# ? Cela peut prendre un certain temps en fonction du nombre de titres trouvés."
confirmDelete = 0, "Souhaitez-vous vraiment supprimer #%s# ? *Ceci sera permanent* !"
confirmDeleteBackupsAll = 0, "Êtes-vous sûr de vouloir supprimer *toutes* vos sauvegardes pour tous vos jeux ?"
confirmDeleteBackupsTitle = 0, "Souhaitez-vous vraiment supprimer toutes les sauvegardes sauvegardées pour #%s#?"
confirmDeleteSaveData = 0, "*ATTENTION* : Ceci *effacera* les données sauvegardées pour #%s# *de votre système*. Êtes-vous sûr de vouloir faire cela ?"
confirmDriveOverwrite = 0, "Le téléchargement de cette sauvegarde depuis le disque dur écrasera celle qui se trouve sur votre carte SD. Continuer?"
confirmOverwrite = 0, "Souhaitez-vous vraiment écraser #%s#?"
confirmResetSaveData = 0, "*ATTENTION* : Cela *réinitialisera* les données de sauvegarde de ce jeu comme s'il n'avait jamais été exécuté auparavant. Êtes-vous sûr de vouloir faire cela ?"
confirmRestore = 0, "Souhaitez-vous vraiment restaurer #%s#?"
debugStatus = 0, "Nombre d'utilisateurs: "
debugStatus = 1, "Utilisateur actuel: "
debugStatus = 2, "Titre actuel: "
debugStatus = 3, "Titre sauvegardé: "
debugStatus = 4, "Type de tri: "
dialogNo = 0, "Non [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Oui [A]"
extrasMenu = 0, "Explorateur SD vers SD"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Supprimer la mise à jour en attente"
extrasMenu = 6, "Terminer le processus"
extrasMenu = 7, "Monter la sauvegarde système"
extrasMenu = 8, "Re-scanner les titres"
extrasMenu = 9, "Monter le processus RomFS"
extrasMenu = 10, "Sauvegarder le répertoire de JKSV"
extrasMenu = 11, "*[DEV]* Forcer la langue en en-US"
fileModeFileProperties = 0, "Chemin: %s\nTaille: %s"
fileModeFolderProperties = 0, "Chemin: %s\nSous-répertoires: %u\nNombre de fichiers: %u\nTaille totale: %s"
fileModeMenu = 0, "Copier vers "
fileModeMenu = 1, "Supprimer"
fileModeMenu = 2, "Renommer"
fileModeMenu = 3, "Créer un nouveau dossier"
fileModeMenu = 4, "Propriétés"
fileModeMenu = 5, "Fermer"
fileModeMenu = 6, "Ajouter aux filtres de chemins"
fileModeMenuMkDir = 0, "Nouveau"
folderMenuNew = 0, "Nouvelle sauvegarde"
helpFolder = 0, "[A] Sélectionner [Y] Restorer [X] Suprimer [ZR] Upload [B] Fermer"
helpSettings = 0, "[A] Basculer [X] Défauts [B] Retour"
helpTitle = 0, "[A] Sélectionner [L][R] Saut de page [Y] Favorie [X] Options du titre [B] Retour"
helpUser = 0, "[A] Sélectionner [X] Options pour l'utilisateur"
holdingText = 0, "(Maintenir) "
holdingText = 1, "(Garder maintenu) "
holdingText = 2, "(Presque fini!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Temps de jeu: %02d:%02d"
infoStatus = 3, "Nombre de fois lancé: %u"
infoStatus = 4, "Auteur: %s"
infoStatus = 5, "Type de sauvegarde: %s"
infoStatus = 6, "Index du Cache: %u"
infoStatus = 7, "Utilisateur: %s"
loadingStartPage = 0, "Chargement..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Paramètres"
onlineErrorConnecting = 0, "Erreur de connection!"
onlineNoUpdates = 0, "Aucune mise à jour disponible."
popAddedToPathFilter = 0, "'#%s#' ajouté aux filtres de chemins."
popCPUBoostEnabled = 0, "CPU Boost activé pour les ZIP."
popChangeOutputError = 0, "#%s# contient des caractères incorrects ou non-ASCII."
popChangeOutputFolder = 0, "#%s# modifié en #%s#"
popDriveFailed = 0, "Echec du démarrage de Google Drive."
popRemoteNotActive = 0, "Remote n'est pas utilisable"
popDriveStarted = 0, "Google Drive lancé avec succès."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Erreur d'ajout du fichier à la sauvegarde!"
popFolderIsEmpty = 0, "Le dossier est vide!"
popProcessShutdown = 0, "#%s# terminé avec succès."
popSVIExported = 0, "SVI Exporté."
popSaveIsEmpty = 0, "Les données de la sauvegarde sont vides!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "Le fichier ZIP est vide!"
saveDataBackupDeleted = 0, "#%s# a été supprimé."
saveDataBackupMovedToTrash = 0, "#%s# a été déplacé dans la corbeille."
saveDataCreatedForUser = 0, "Données de sauvegarde créée pour %s!"
saveDataCreationFailed = 0, "Echec de la création des données de sauvegarde!"
saveDataDeleteAllUser = 0, "*Souhaitez-vous vraiment supprimer toutes les données de sauvegarde pour %s?*"
saveDataDeleteSuccess = 0, "Données de la sauvegarde pour #%s# Supprimée!"
saveDataExtendFailed = 0, "Echec de l'extention des données de la sauvegarde."
saveDataExtendSuccess = 0, "Données de la sauvegarde pour #%s# étendue!"
saveDataIndexText = 0, "Index de la sauvegarde: "
saveDataNoneFound = 0, "Aucune sauvegarde trouvée pour #%s#!"
saveDataResetSuccess = 0, "Sauvegarde pour #%s# réinitialisée!"
saveDataTypeText = 0, "Sauvegarde système"
saveDataTypeText = 1, "Données de sauvegarde"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Sauvegarde de périphérique"
saveDataTypeText = 4, "Stockage temporaire"
saveDataTypeText = 5, "Stockage cache"
saveDataTypeText = 6, "BCAT système"
saveTypeMainMenu = 0, "Périphérique"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "Système"
saveTypeMainMenu = 4, "BCAT système"
saveTypeMainMenu = 5, "Stockage SysTemp"
settingsMenu = 0, "Vider la corbeille"
settingsMenu = 1, "Vérifier les mises à jour"
settingsMenu = 2, "Définir le dossier de sortie de JKSV"
settingsMenu = 3, "Modifier les titres de la liste noire"
settingsMenu = 4, "Supprimer toutes les sauvegardes sauvegardées"
settingsMenu = 5, "Inclure les sauvegardes du matériel avec les utilisateurs: "
settingsMenu = 6, "Sauvegarder automatiquement avant la restauration: "
settingsMenu = 7, "Nom automatique des sauvegardes: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Maintenir pour supprimer: "
settingsMenu = 10, "Maintenir pour restaurer: "
settingsMenu = 11, "Maintenir pour écraser: "
settingsMenu = 12, "Forcer le montage: "
settingsMenu = 13, "Compte des sauvegardes système: "
settingsMenu = 14, "Activer l'écriture pour les sauvegardes système: "
settingsMenu = 15, "Utiliser directement les commandes FS : "
settingsMenu = 16, "Exporter les sauvegardes en ZIP: "
settingsMenu = 17, "Forcer l'utilisation de l'anglais: "
settingsMenu = 18, "Activer la corbeille: "
settingsMenu = 19, "Type de tri des titres: "
settingsMenu = 20, "Échelle d'animation: "
settingsOff = 0, "Désactivé"
settingsOn = 0, ">Activé>"
sortType = 0, "Alphabétique"
sortType = 1, "Temps de jeu"
sortType = 2, "Joué dernièrement"
swkbdEnterName = 0, "Entrez un nouveau nom"
swkbdExpandSize = 0, "Entrez la nouvelle taille en MB"
swkbdMkDir = 0, "Entrez un nom de dossier"
swkbdNewSafeTitle = 0, "Entrez un nouveau nom de dossier"
swkbdProcessID = 0, "Entrez l'ID du processus"
swkbdRename = 0, "Entrez un nouveau nom pour l'objet"
swkbdSaveIndex = 0, "Entrez l'index du cache"
swkbdSetWorkDir = 0, "Entrez un nouveau chemin de sortie"
swkbdSysSavID = 0, "Entrez l'ID de la sauvegarde système"
threadStatusAddingFileToZip = 0, "Ajout de '#%s#' au ZIP..."
threadStatusCalculatingSaveSize = 0, "Calcul de la taille de la sauvegarde..."
threadStatusCheckingForUpdate = 0, "Vérification de mises à jour..."
threadStatusCompressingSaveForUpload = 0, "Compression de #%s# pour l'upload..."
threadStatusCopyingFile = 0, "Copie de '#%s#'..."
threadStatusCreatingSaveData = 0, "Création de la sauvegarde pour #%s#..."
threadStatusDecompressingFile = 0, "Décompression de '#%s#'..."
threadStatusDeletingFile = 0, "Suppression..."
threadStatusDeletingSaveData = 0, "Supression de la sauvegarde pour #%s#..."
threadStatusDeletingUpdate = 0, "Suppression de la mise à jour en attente..."
threadStatusDownloadingFile = 0, "Téléchargement de #%s#..."
threadStatusDownloadingUpdate = 0, "Téléchargement de la mise à jour..."
threadStatusExtendingSaveData = 0, "Extention de la sauvegarde pour #%s#..."
threadStatusGetDirProps = 0, "Obtention des propriétés du dossier..."
threadStatusOpeningFolder = 0, "Ouverture de '#%s#'..."
threadStatusPackingJKSV = 0, "Ecriture du contenu du répertoire JKSV dans le ZIP..."
threadStatusResettingSaveData = 0, "Réinitialisation de la sauvegarde..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Upload de #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Liste noir"
titleOptions = 2, "Changer le dossier de sortie"
titleOptions = 3, "Ouvrir en mode fichier"
titleOptions = 4, "Supprimer toutes les sauvegardes de la sauvegarde"
titleOptions = 5, "Réinitialiser la sauvegarde"
titleOptions = 6, "Supprimer la sauvegarde"
titleOptions = 7, "Etendre la sauvegarde"
titleOptions = 8, "Exporter SVI"
translationMainPage = 0, "Traduction: "
userOptions = 0, "Dumper tout pour"
userOptions = 1, "Sauvegarder la sauvegarde"
userOptions = 2, "Sauvegarder toutes les sauvegardes"
userOptions = 3, "Supprimer toutes les sauvegardes de l'utilisateur"

171
romfs/lang/it.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "SimoCasa"
confirmBlacklist = 0, "Sei sicuro di voler aggiungere #%s# alla tua blacklist?"
confirmCopy = 0, "Sei sicuro di voler copiare #%s# in #%s#?"
confirmCreateAllSaveData = 0, "Sei sicuro di voler creare tutti i dati di salvataggio su questo sistema per #%s#? Può metterci un po' di tempo in base ai titoli trovati."
confirmDelete = 0, "Sei sicuro di voler cancellare #%s#? *È permanente*!"
confirmDeleteBackupsAll = 0, "Sei sicuro di voler cancellare *tutti* i tuoi backup dei salvataggi di gioco?"
confirmDeleteBackupsTitle = 0, "Sei sicuro di voler cancellare tutti i salvataggi di backup di #%s#?"
confirmDeleteSaveData = 0, "*ATTENZIONE*: Questo *cancellerà* il salvataggio di #%s# *dal tuo sistema*. Sei sicuro di volerlo fare?"
confirmDriveOverwrite = 0, "Scaricando questo backup da Drive sovrascriverai quello nella tua scheda SD. Vuoi continuare?"
confirmOverwrite = 0, "Sei sicuro di voler sovrascrivere #%s#?"
confirmResetSaveData = 0, "*ATTENZIONE*: Questo *resetterà* i dati di salvataggio per questo gioco come se non fosse mai stato eseguito prima. Sei sicuro di volerlo fare?"
confirmRestore = 0, "Sei sicuro di voler ripristinare #%s#?"
debugStatus = 0, "Numero Utenti: "
debugStatus = 1, "Utente Corrente: "
debugStatus = 2, "Titolo Corrente: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Tipo Ordinamento: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Si [A]"
extrasMenu = 0, "Browser da SD a SD"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Rimuovi Aggiornamento in attesa"
extrasMenu = 6, "Termina Processo"
extrasMenu = 7, "Monta Salvataggio di Sistema"
extrasMenu = 8, "Riscansiona Titoli"
extrasMenu = 9, "Monta Processo RomFS"
extrasMenu = 10, "Backup cartella JKSV"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Percorso: %s\nDimensione: %s"
fileModeFolderProperties = 0, "Percorso: %s\nSottocartelle: %u\nNumero File: %u\nDimensione totale: %s"
fileModeMenu = 0, "Copia in "
fileModeMenu = 1, "Elimina"
fileModeMenu = 2, "Rinomina"
fileModeMenu = 3, "Crea Cartella"
fileModeMenu = 4, "Proprietà"
fileModeMenu = 5, "Chiudi"
fileModeMenu = 6, "Aggiungi ai Filtri del Percorso"
fileModeMenuMkDir = 0, "Nuovo"
folderMenuNew = 0, "Nuovo Backup"
helpFolder = 0, "[A] Seleziona [Y] Ripristina [X] Cancella [ZR] Upload [B] Chiudi"
helpSettings = 0, "[A] Attiva/Disattiva [X] Predefinite [B] Indietro"
helpTitle = 0, "[A] Seleziona [L][R] Jump [Y] Preferiti [X] Opzioni Titolo [B] Indietro"
helpUser = 0, "[A] Seleziona [Y] Dump di tutti i Salvataggi [X] Opzioni Utente"
holdingText = 0, "(Tieni Premuto) "
holdingText = 1, "(Continua a Premere) "
holdingText = 2, "(Ci sei quasi!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Tempo di Gioco: %02d:%02d"
infoStatus = 3, "Avvii Totali: %u"
infoStatus = 4, "Casa Editrice: %s"
infoStatus = 5, "Tipo Salvataggio: %s"
infoStatus = 6, "Indice della Cache: %u"
infoStatus = 7, "Utente: %s"
loadingStartPage = 0, "Caricamento..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Impost."
onlineErrorConnecting = 0, "Errore di Connessione!"
onlineNoUpdates = 0, "Nessun Aggiornamento Disponibile."
popAddedToPathFilter = 0, "'#%s#' aggiunto ai filtri del percorso."
popCPUBoostEnabled = 0, "CPU Boost Abilitato per ZIP."
popChangeOutputError = 0, "#%s# contiene caratteri illeciti o non-ASCII."
popChangeOutputFolder = 0, "#%s# cambiato in #%s#"
popDriveFailed = 0, "Impossibile avviare Google Drive."
popRemoteNotActive = 0, "Remote non è Disponibile"
popDriveStarted = 0, "Google Drive avviato con successo."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Errore durante il commiting del file da salvare!"
popFolderIsEmpty = 0, "La cartella è vuota!"
popProcessShutdown = 0, "#%s# spento con successo."
popSVIExported = 0, "SVI Esportato."
popSaveIsEmpty = 0, "Il salvataggio è vuoto!"
popTrashEmptied = 0, "Cestino Svuotato"
popZipIsEmpty = 0, "Il file ZIP è vuoto!"
saveDataBackupDeleted = 0, "#%s# è stato cancellato."
saveDataBackupMovedToTrash = 0, "#%s# è stato spostato nel cestino."
saveDataCreatedForUser = 0, "Salvataggio creato per %s!"
saveDataCreationFailed = 0, "Creazione salvataggio fallita!"
saveDataDeleteAllUser = 0, "*SEI SICURO DI VOLER CANCELLARE TUTTI I SALVATAGGI DI %s?*"
saveDataDeleteSuccess = 0, "Salvataggio di #%s# cancellato!"
saveDataExtendFailed = 0, "Estensione salvataggio fallita."
saveDataExtendSuccess = 0, "Salvataggio di #%s# esteso!"
saveDataIndexText = 0, "Indice di backup: "
saveDataNoneFound = 0, "Nessun Salvataggio trovato per #%s#!"
saveDataResetSuccess = 0, "Salvataggio di #%s# resettato!"
saveDataTypeText = 0, "Sistema"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Dispositivo"
saveDataTypeText = 4, "Temporanea"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Disposit."
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "Sistema"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Svuota Cestino"
settingsMenu = 1, "Controllo Aggiornamenti"
settingsMenu = 2, "Imposta cartella JKSV di Output"
settingsMenu = 3, "Modifica titoli in Blacklist"
settingsMenu = 4, "Cancella tutti i Salvataggi di Backup"
settingsMenu = 5, "Includi Salvataggi Dispositivo con Utenti: "
settingsMenu = 6, "Backup Automatico nel Ripristino: "
settingsMenu = 7, "Auto-Rinominazione Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Tieni premuto per eliminare: "
settingsMenu = 10, "Tieni premuto per ripristinare: "
settingsMenu = 11, "Tieni premuto per sovrascrivere: "
settingsMenu = 12, "Forza Montaggio: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Abilita scrittura su Salvataggi Sistema: "
settingsMenu = 15, "Usa comandi FS direttamente: "
settingsMenu = 16, "Esporta salvataggio in ZIP: "
settingsMenu = 17, "Forza utilizzo Inglese: "
settingsMenu = 18, "Abilita Cestino: "
settingsMenu = 19, "Tipo di ordinamento del Titolo: "
settingsMenu = 20, "Scala di Animazione: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alfabetico"
sortType = 1, "Tempo di Gioco"
sortType = 2, "Ultima Partita"
swkbdEnterName = 0, "Inserisci nuovo nome"
swkbdExpandSize = 0, "Inserisci nuova dimensione in MB"
swkbdMkDir = 0, "Inserisci nome cartella"
swkbdNewSafeTitle = 0, "Inserisci una nuova cartella di output"
swkbdProcessID = 0, "Inserisci Process ID"
swkbdRename = 0, "Inserisci un nuovo nome per elemento"
swkbdSaveIndex = 0, "Inserisci l'Indice della Cache"
swkbdSetWorkDir = 0, "Inserisci un nuovo Percorso Output"
swkbdSysSavID = 0, "Inserisci System Save ID"
threadStatusAddingFileToZip = 0, "Aggiungi '#%s#' a ZIP..."
threadStatusCalculatingSaveSize = 0, "Calcolo dimensione salvataggio..."
threadStatusCheckingForUpdate = 0, "Controllo Aggiornamenti..."
threadStatusCompressingSaveForUpload = 0, "Compressione di #%s# per l'upload..."
threadStatusCopyingFile = 0, "Copiando '#%s#'..."
threadStatusCreatingSaveData = 0, "Creazione salvataggio per #%s#..."
threadStatusDecompressingFile = 0, "Decompressione '#%s#'..."
threadStatusDeletingFile = 0, "Cancellando..."
threadStatusDeletingSaveData = 0, "Cancellazione salvataggio di #%s#..."
threadStatusDeletingUpdate = 0, "Cancellazione dell'aggiornamento in sospeso..."
threadStatusDownloadingFile = 0, "Download di #%s#..."
threadStatusDownloadingUpdate = 0, "Download aggiornamento..."
threadStatusExtendingSaveData = 0, "Estensione Salvataggio per #%s#..."
threadStatusGetDirProps = 0, "Ottenimento le proprietà della cartella..."
threadStatusOpeningFolder = 0, "Apertura '#%s#'..."
threadStatusPackingJKSV = 0, "Scrittura del contenuto della cartella JKSV su ZIP..."
threadStatusResettingSaveData = 0, "Reset salvataggio..."
threadStatusSavingTranslations = 0, "Salvataggio del file master..."
threadStatusUploadingFile = 0, "Upload di #%s#..."
titleOptions = 0, "Informazioni"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Cambia Cartella Output"
titleOptions = 3, "Apri in Modalità File"
titleOptions = 4, "Elimina tutti i Salvataggi di Backup"
titleOptions = 5, "Resetta Salvataggio"
titleOptions = 6, "Elimina Salvataggio"
titleOptions = 7, "Estendi Salvataggio"
titleOptions = 8, "Esporta SVI"
translationMainPage = 0, "Traduzione: "
userOptions = 0, "Dump di tutto per "
userOptions = 1, "Crea salvataggio"
userOptions = 2, "Crea tutti salvataggi"
userOptions = 3, "Cancella tutti salvataggi Utente"

215
romfs/lang/ja.txt Normal file
View File

@ -0,0 +1,215 @@
author = 0, "NULL"
confirmBlacklist = 0, "ブラックリストに#%s#を追加してもよろしいですか?"
confirmCopy = 0, "#%s#を#%s#にコピーしてもよろしいですか?"
confirmCreateAllSaveData = 0, "このシステムで#%s#のすべてのセーブデータを作成してもよろしいですか?見つかったタイトルの数によっては、時間がかかる場合があります。"
confirmDelete = 0, "#%s#を削除してもよろしいですか? これは永続的です!"
confirmDeleteBackupsAll = 0, "全てのゲームのセーブバックアップを全て削除してもよろしいですか?"
confirmDeleteBackupsTitle = 0, "#%s#のすべてのセーブデータのバックアップを削除してもよろしいですか?"
confirmDeleteSaveData = 0, "警告: これにより、#%s#のセーブデータがシステムから消去されます。 これを実行してもよろしいですか?"
#CHANGED=============================================>
confirmDriveOverwrite = 0, "このバックアップをドライブからダウンロードすると、SDカードにあるバックアップが上書きされます。続行しますか"
#<====================================================
confirmOverwrite = 0, "#%s#を上書きしてもよろしいですか?"
confirmResetSaveData = 0, "警告: これにより、このゲームのセーブデータが、以前に実行されたことがないかのようにリセットされます。これを実行してもよろしいですか?"
confirmRestore = 0, "#%s#を復元してもよろしいですか?"
#CHANGED=============================================>
debugStatus = 0, "ユーザー数: "
debugStatus = 1, "現在のユーザー: "
debugStatus = 2, "現在のタイトル: "
debugStatus = 3, "安全なタイトル: "
debugStatus = 4, "ソートの種類: "
#<====================================================
dialogNo = 0, "いいえ [B]"
dialogOK = 0, "了解 [A]"
dialogYes = 0, "はい [A]"
extrasMenu = 0, "SDからSDブラウザ"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "保留中のアップデータを削除する"
extrasMenu = 6, "プロセスを終了します"
extrasMenu = 7, "マウントシステムSave"
extrasMenu = 8, "タイトルの再スキャン"
extrasMenu = 9, "マウントプロセスRomFS"
extrasMenu = 10, "JKSVフォルダをバックアップ"
extrasMenu = 11, "*[DEV]* 出力en-US"
fileModeFileProperties = 0, "パス: %s\n容量: %s"
fileModeFolderProperties = 0, "パス: %s\nサブフォルダ: %u\nファイル数: %u\n全体の大きさ: %s"
fileModeMenu = 0, "コピー先 "
fileModeMenu = 1, "削除"
fileModeMenu = 2, "リネーム"
fileModeMenu = 3, "ディレクトリ作成"
fileModeMenu = 4, "プロパティ"
fileModeMenu = 5, "閉じる"
fileModeMenu = 6, "パスフィルターに追加"
fileModeMenuMkDir = 0, "新規"
folderMenuNew = 0, "新規バックアップ"
#CHANGED=============================================>
helpFolder = 0, "[A] 選択 [Y] リストア [X] 削除 [ZR] アップロード [B] 閉じる"
#<====================================================
helpSettings = 0, "[A] トグル [X] デフォルト [B] 戻る"
helpTitle = 0, "[A] 選択 [L][R] ジャンプ [Y] お気に入り [X] タイトルオプション [B] 戻る"
helpUser = 0, "[A] 選択 [X] ユーザー設定"
holdingText = 0, "(所有) "
holdingText = 1, "(保持し続けます) "
holdingText = 2, "(もうすぐです!) "
#CHANGED=============================================>
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "プレイ時間: %02d:%02d"
infoStatus = 3, "起動回数: %u"
infoStatus = 4, "出版社: %s"
infoStatus = 5, "セーブの種類: %s"
infoStatus = 6, "キャッシュインデックス: %u"
infoStatus = 7, "ユーザー: %s"
#<=====================================================
loadingStartPage = 0, "読み込み中..."
mainMenuExtras = 0, "エクストラ"
mainMenuSettings = 0, "設定"
onlineErrorConnecting = 0, "接続エラー!"
onlineNoUpdates = 0, "利用可能なアップデートはありません。"
popAddedToPathFilter = 0, "'#%s#' パスフィルターに追加されました。"
popCPUBoostEnabled = 0, "ZIPでCPUブーストが有効になっています。"
popChangeOutputError = 0, "#%s# は、不正な文字または非ASCII文字が含まれています。"
popChangeOutputFolder = 0, "#%s# から #%s# に変更"
#CHANGED=============================================>
popDriveFailed = 0, "Google Driveの起動に失敗しました。"
popRemoteNotActive = 0, "Remoteドライブは使用できません"
popDriveStarted = 0, "Google Driveが正常に起動しました。"
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
#<====================================================
popErrorCommittingFile = 0, "保存するファイルのコミット中にエラーが発生しました!"
popFolderIsEmpty = 0, "フォルダは空です!"
popProcessShutdown = 0, "#%s# 正常にシャットダウンしました。"
#CHANGED=============================================>
popSVIExported = 0, "SVIをエクスポートしました。"
#<====================================================
popSaveIsEmpty = 0, "セーブデータは空です!"
popTrashEmptied = 0, "ゴミ箱を空にする"
popZipIsEmpty = 0, "ZIPファイルは空です!"
saveDataBackupDeleted = 0, "#%s#が削除されました。"
saveDataBackupMovedToTrash = 0, "#%s#はゴミ箱に移動されました"
saveDataCreatedForUser = 0, "#%s#用に作成されたデータを保存する!"
saveDataCreationFailed = 0, "セーブデータの作成に失敗しました!"
saveDataDeleteAllUser = 0, "#%s#のすべての保存データを削除してもよろしいですか?*"
saveDataDeleteSuccess = 0, "#%s#のセーブデータは削除されました!"
saveDataExtendFailed = 0, "セーブデータの拡張に失敗しました。"
saveDataExtendSuccess = 0, "#%s#のセーブデータを拡張!"
saveDataIndexText = 0, "存檔索引號:"
saveDataNoneFound = 0, "#%s#のセーブデータが見つかりません!"
saveDataResetSuccess = 0, "#%s#のセーブデータをリセット!"
saveDataTypeText = 0, "システム\n"
saveDataTypeText = 1, "アカウント\n"
saveDataTypeText = 2, "BCAT\n"
saveDataTypeText = 3, "デバイス\n"
saveDataTypeText = 4, "一時的なもの\n"
saveDataTypeText = 5, "キャッシュ\n"
saveDataTypeText = 6, "システムBCAT\n"
saveTypeMainMenu = 0, "デバイス"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "キャッシュ"
saveTypeMainMenu = 3, "システム"
saveTypeMainMenu = 4, "システム BCAT"
saveTypeMainMenu = 5, "システマティックストレージ機器"
settingsMenu = 0, "ゴミ箱を空にする"
settingsMenu = 1, "アップデートを確認する"
settingsMenu = 2, "JKSVセーブデータ保存出力フォルダを設定する"
settingsMenu = 3, "ブラックリストに載っているタイトルを編集する"
settingsMenu = 4, "すべての保存バックアップを削除する"
settingsMenu = 5, "ユーザーと一緒にデバイスのセーブデータを含める: "
settingsMenu = 6, "復元時の自動バックアップ: "
settingsMenu = 7, "自動名前バックアップ: "
settingsMenu = 8, "オーバークロック/CPUブースト: "
settingsMenu = 9, "削除するために保持: "
settingsMenu = 10, "保持して復元: "
settingsMenu = 11, "上書きするために保持: "
settingsMenu = 12, "フォースマウント: "
settingsMenu = 13, "アカウントシステムセーブデータ: "
settingsMenu = 14, "システムセーブデータへの書き込みを有効にする: "
settingsMenu = 15, "FSコマンドを直接使用する: "
settingsMenu = 16, "セーブデータをZIPにエクスポート: "
settingsMenu = 17, "英語を強制的に使用する: "
settingsMenu = 18, "ごみ箱を有効にする: "
settingsMenu = 19, "タイトルの並べ替えタイプ: "
settingsMenu = 20, "アニメーションスケール: "
settingsOff = 0, "オフ"
settingsOn = 0, ">オン>"
sortType = 0, "アルファベット順"
sortType = 1, "プレイ時間"
sortType = 2, "最後にプレイ"
swkbdEnterName = 0, "新しい名前を入力してください"
swkbdExpandSize = 0, "新しいサイズをMBで入力"
swkbdMkDir = 0, "フォルダ名を入力してください"
swkbdNewSafeTitle = 0, "新規出力先フォルダを入力してください"
swkbdProcessID = 0, "プロセスIDを入力してください"
swkbdRename = 0, "アイテムの新しい名前を入力してください"
swkbdSaveIndex = 0, "キャッシュインデックスを入力してください"
swkbdSetWorkDir = 0, "新しい出力パスを入力してください"
swkbdSysSavID = 0, "システムセーブIDを入力してください"
threadStatusAddingFileToZip = 0, "#%s#をZIPに追加中..."
#CHANGED=============================================>
threadStatusCalculatingSaveSize = 0, "保存データサイズの算出中..."
#<====================================================
threadStatusCheckingForUpdate = 0, "アップデートの確認中..."
#CHANGED=============================================>
threadStatusCompressingSaveForUpload = 0, "アップロードのために #%s# を圧縮中..."
#<====================================================
threadStatusCopyingFile = 0, "#%s#をコピーしています..."
threadStatusCreatingSaveData = 0, "#%s#のセームデータの作成中..."
threadStatusDecompressingFile = 0, "#%s#の解凍中..."
threadStatusDeletingFile = 0, "削除中..."
threadStatusDeletingSaveData = 0, "#%s#のセーブデータを削除中..."
threadStatusDeletingUpdate = 0, "保留中のアップデータを削除中..."
#CHANGED=============================================>
threadStatusDownloadingFile = 0, "ダウンロード中 #%s#..."
#<====================================================
threadStatusDownloadingUpdate = 0, "アップデータをダウンロード中..."
threadStatusExtendingSaveData = 0, "#%s#のセーブデータを拡張中..."
threadStatusGetDirProps = 0, "フォルダプロパティの取得中..."
threadStatusOpeningFolder = 0, "#%s#を開き中..."
threadStatusPackingJKSV = 0, "JKSVフォルダの内容をZIPに書き込み中..."
threadStatusResettingSaveData = 0, "セーブデータをリストアしています..."
threadStatusSavingTranslations = 0, "ファイルマスタの保存中..."
#CHANGED=============================================>
threadStatusUploadingFile = 0, "アップロード中 #%s#..."
#<====================================================
titleOptions = 0, "インフォメーション"
titleOptions = 1, "ブラックリスト"
titleOptions = 2, "出力フォルダを変更する"
titleOptions = 3, "ファイルモードで開く"
titleOptions = 4, "すべてのセーブデータバックアップを削除する"
titleOptions = 5, "セーブデータをリセット"
titleOptions = 6, "セーブデータを削除"
titleOptions = 7, "セーブデータを拡張"
#CHANGED=============================================>
titleOptions = 8, "エクスポートSVI"
#<====================================================
translationMainPage = 0, "翻訳: "
userOptions = 0, "すべてをダンプする "
userOptions = 1, "セーブデータを作成"
userOptions = 2, "全てのセーブデータを作成"
userOptions = 3, "全てのユーザーセーブデータを削除"

171
romfs/lang/ko.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "블랙리스트에 #%s#을(를) 추가하겠습니까?"
confirmCopy = 0, "#%s#을(를) #%s#에 복사하겠습니까?"
confirmCreateAllSaveData = 0, "이 시스템에서 #%s#에 대한 모든 저장 데이터를 생성하겠습니까? 검색된 타이틀 수에 따라 시간이 걸릴 수 있습니다."
confirmDelete = 0, "#%s#을(를) 삭제하겠습니까? *영구적입니다*!"
confirmDeleteBackupsAll = 0, "모든 게임에 대한 *모든* 저장 백업을 삭제하겠습니까?"
confirmDeleteBackupsTitle = 0, "#%s#에 대한 모든 백업 백업을 삭제하겠습니까?"
confirmDeleteSaveData = 0, "경고*: 이렇게 하면 #%s#에 대한 저장 데이터가 *시스템에서* 지워집니다. 정말 하겠습니까?"
confirmDriveOverwrite = 0, "드라이브에서 이 백업을 다운로드하면 SD 카드에 있는 백업을 덮어씁니다. 계속합니까?"
confirmOverwrite = 0, "#%s#을(를) 덮어쓰시겠습니까?"
confirmResetSaveData = 0, "경고*: 이렇게 하면 이 게임의 저장 데이터가 이전에 실행되지 않은 것처럼 *리셋됩니다*. 정말 하겠습니까?"
confirmRestore = 0, "#%s#을(를) 복원하겠습니까?"
debugStatus = 0, "사용자 수: "
debugStatus = 1, "현자 사용자: "
debugStatus = 2, "현재 제목: "
debugStatus = 3, "안전 제목: "
debugStatus = 4, "정렬 유형: "
dialogNo = 0, "아니오 [B]"
dialogOK = 0, "확인 [A]"
dialogYes = 0, "예 [A]"
extrasMenu = 0, "SD에서 SD 브라우저로"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: 안전"
extrasMenu = 3, "BIS: 시스템"
extrasMenu = 4, "BIS: 사용자"
extrasMenu = 5, "보류 중인 업데이트 제거"
extrasMenu = 6, "프로세스 종료"
extrasMenu = 7, "시스템 저장 인식"
extrasMenu = 8, "제목 다시 검색"
extrasMenu = 9, "프로세스 RomFS 인식"
extrasMenu = 10, "JKSV 폴더 백업"
extrasMenu = 11, "*[DEV]* 출력 en-US"
fileModeFileProperties = 0, "경로: %s\n크기: %s"
fileModeFolderProperties = 0, "경로: %s\n하위 폴더: %u\n파일 수: %u\n전체 크기: %s"
fileModeMenu = 0, "에게 복사 "
fileModeMenu = 1, "삭제"
fileModeMenu = 2, "이름 바꾸기"
fileModeMenu = 3, "폴더 만들기"
fileModeMenu = 4, "속성"
fileModeMenu = 5, "닫기"
fileModeMenu = 6, "경로 필터에 추가"
fileModeMenuMkDir = 0, "새로 만들기"
folderMenuNew = 0, "새 백업"
helpFolder = 0, "[A] 선택 [Y] 복원 [X] 삭제 [ZR] 업로드 [B] 닫기"
helpSettings = 0, "[A] 전환 [X] 기본 [B] 뒤로가기"
helpTitle = 0, "[A] 선택 [L][R] 점프 [Y] 즐겨찾기 [X] 제목 옵션 [B] 뒤로가기"
helpUser = 0, "[A] 선택 [Y] 모든 세이브 덤프 [X] 사용자 옵션"
holdingText = 0, "(길게 누르기) "
holdingText = 1, "(계속 누르기) "
holdingText = 2, "(거의 완료!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "즐긴 시간: %02d:%02d"
infoStatus = 3, "총 실행 수: %u"
infoStatus = 4, "발행자: %s"
infoStatus = 5, "저장 유형: %s"
infoStatus = 6, "캐시 인덱스: %u"
infoStatus = 7, "사용자: %s"
infoStatus = 9, ""
loadingStartPage = 0, "로드 중..."
mainMenuExtras = 0, "추가"
mainMenuSettings = 0, "설정"
onlineErrorConnecting = 0, "연결 오류!"
onlineNoUpdates = 0, "사용 가능한 업데이트가 없습니다."
popAddedToPathFilter = 0, "'#%s#'이(가) 경로 필터에 추가되었습니다."
popCPUBoostEnabled = 0, "ZIP를 위한 CPU 부스트가 활성화되었습니다."
popChangeOutputError = 0, "#%s#에 잘못된 또는 ASCII가 아닌 문자가 포함되어 있습니다."
popChangeOutputFolder = 0, "#%s#이(가) #%s#로 변경되었습니다."
popDriveFailed = 0, "구글 드라이브 시작에 실패했습니다."
popRemoteNotActive = 0, "구글 드라이브를 사용할 수 없습니다."
popDriveStarted = 0, "구글 드라이브가 성공적으로 시작되었습니다."
popWebdavStarted = 0, "Webdav가 성공적으로 시작되었습니다."
popWebdavFailed =, 0, "Webdav를 시작하지 못했습니다."
popErrorCommittingFile = 0, "저장할 파일을 커밋하는 동안 오류가 발생했습니다!"
popFolderIsEmpty = 0, "폴더가 비어 있습니다!"
popProcessShutdown = 0, "#%s#이(가) 성공적으로 종료되었습니다."
popSVIExported = 0, "SVI를 내보냈습니다."
popSaveIsEmpty = 0, "저장 데이터가 비어 있습니다!"
popTrashEmptied = 0, "휴지통이 비었습니다."
popZipIsEmpty = 0, "ZIP 파일이 비어 있습니다!"
saveDataBackupDeleted = 0, "#%s#이(가) 삭제되었습니다."
saveDataBackupMovedToTrash = 0, "#%s#이(가) 휴지통으로 이동되었습니다."
saveDataCreatedForUser = 0, "%s에 대해 생성된 데이터 저장!"
saveDataCreationFailed = 0, "저장 데이터 생성 실패!"
saveDataDeleteAllUser = 0, "*%s에 대한 모든 저장 데이터를 삭제하겠습니까?*"
saveDataDeleteSuccess = 0, "#%s#에 대한 저장 데이터가 삭제되었습니다!"
saveDataExtendFailed = 0, "저장 데이터 확장에 실패했습니다."
saveDataExtendSuccess = 0, "#%s#에 대한 데이터 저장이 확장되었습니다!"
saveDataNoneFound = 0, "#%s#에 대한 저장을 찾을 수 없습니다!"
saveDataResetSuccess = 0, "#%s# 재설정을 위해 저장합니다!"
saveDataTypeText = 0, "시스템"
saveDataTypeText = 1, "계정"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "장치"
saveDataTypeText = 4, "임시"
saveDataTypeText = 5, "캐시"
saveDataTypeText = 6, "시스템 BCAT"
saveTypeMainMenu = 0, "장치"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "캐시"
saveTypeMainMenu = 3, "시스템"
saveTypeMainMenu = 4, "시스템 BCAT"
saveTypeMainMenu = 5, "SysTemp 스토리지템"
settingsMenu = 0, "휴지통 비우기"
settingsMenu = 1, "업데이트 확인"
settingsMenu = 2, "JKSV 저장 출력 폴더 설정"
settingsMenu = 3, "블랙리스트에 있는 제목 편집"
settingsMenu = 4, "모든 저장 백업 삭제"
settingsMenu = 5, "사용자와 함께 장치 저장 포함: "
settingsMenu = 6, "복원 시 자동 백업: "
settingsMenu = 7, "자동 이름 백업: "
settingsMenu = 8, "오버클럭/CPU 부스트: "
settingsMenu = 9, "삭제하려면 길게 누르기: "
settingsMenu = 10, "복원하려면 길게 누르기: "
settingsMenu = 11, "덮어쓰려면 길게 누르기: "
settingsMenu = 12, "강제 인식: "
settingsMenu = 13, "계정 시스템 저장: "
settingsMenu = 14, "시스템 저장에 쓰기 활성화: "
settingsMenu = 15, "FS 명령을 직접 사용: "
settingsMenu = 16, "저장된 파일을 ZIP으로 내보내기: "
settingsMenu = 17, "강제 영어 사용: "
settingsMenu = 18, "휴지통 활성화: "
settingsMenu = 19, "제목 정렬 유형: "
settingsMenu = 20, "애니메이션 스케일: "
settingsOff = 0, "끔"
settingsOn = 0, ">켬>"
sortType = 0, "알파벳"
sortType = 1, "즐긴 시간"
sortType = 2, "마지막 실행"
swkbdEnterName = 0, "새 이름 입력"
swkbdExpandSize = 0, "새 MB 크기 입력"
swkbdMkDir = 0, "폴더 이름 입력"
swkbdNewSafeTitle = 0, "새 출력 폴더 입력"
swkbdProcessID = 0, "프로세스 ID 입력"
swkbdRename = 0, "항목의 새 이름을 입력"
swkbdSaveIndex = 0, "캐시 인덱스 입력"
swkbdSetWorkDir = 0, "새 출력 경로 입력"
swkbdSysSavID = 0, "시스템 저장 ID 입력"
threadStatusAddingFileToZip = 0, "'#%s#'을(를) ZIP에 추가하는 중..."
threadStatusCalculatingSaveSize = 0, "저장 데이터 크기 계산 중..."
threadStatusCheckingForUpdate = 0, "업데이트 확인 중..."
threadStatusCompressingSaveForUpload = 0, "업로드를 위해 #%s# 압축 중..."
threadStatusCopyingFile = 0, "'#%s#' 복사 중..."
threadStatusCreatingSaveData = 0, "#%s#에 대한 저장 데이터 생성 중..."
threadStatusDecompressingFile = 0, "'#%s#' 압축 해제 중..."
threadStatusDeletingFile = 0, "삭제 중..."
threadStatusDeletingSaveData = 0, "#%s#에 대한 저장 데이터 삭제 중..."
threadStatusDeletingUpdate = 0, "대기 중인 업데이트 삭제 중..."
threadStatusDownloadingFile = 0, "#%s# 다운로드 중..."
threadStatusDownloadingUpdate = 0, "업데이트 다운로드 중..."
threadStatusExtendingSaveData = 0, "#%s#에 대한 저장 데이터 확장 중..."
threadStatusGetDirProps = 0, "폴더 속성 가져오기..."
threadStatusOpeningFolder = 0, "'#%s#'을(를) 여는 중..."
threadStatusPackingJKSV = 0, "JKSV 폴더 내용을 ZIP에 쓰는 중..."
threadStatusResettingSaveData = 0, "저장 데이터 재설정 중..."
threadStatusSavingTranslations = 0, "마스터 파일 저장 중..."
threadStatusUploadingFile = 0, "#%s# 업로드 중..."
titleOptions = 0, "정보"
titleOptions = 1, "블랙리스트"
titleOptions = 2, "출력 폴더 변경"
titleOptions = 3, "파일 모드에서 열기"
titleOptions = 4, "모든 저장 백업 삭제"
titleOptions = 5, "저장 데이터 재설정"
titleOptions = 6, "저장 데이터 삭제"
titleOptions = 7, "저장 데이터 확장"
titleOptions = 8, "SVI 내보내기"
translationMainPage = 0, "번역: "
userOptions = 0, "에 대해 모두 덤프 "
userOptions = 1, "저장 데이터 생성"
userOptions = 2, "모든 저장 데이터 생성"
userOptions = 3, "모든 사용자 저장 삭제"

171
romfs/lang/nl.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

156
romfs/lang/pt-BR.txt Normal file
View File

@ -0,0 +1,156 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: "
infoStatus = 1, "SID: "
infoStatus = 2, "Play Time: "
infoStatus = 3, "Total Launches: "
infoStatus = 4, "User Count: "
infoStatus = 5, "Current User: "
infoStatus = 6, "Current Title: "
infoStatus = 7, "Safe Title: "
infoStatus = 8, "Sort Type: "
infoStatus = 9, "Saving the file master..."
infoStatus = 10, "Deleting..."
infoStatus = 11, "Trash emptied."
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSaveIsEmpty = 0, "Save data is empty!"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataIndexText = 0, "存檔索引號:"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "系統存檔\n"
saveDataTypeText = 1, "用戶存檔\n"
saveDataTypeText = 2, "BCAT存檔\n"
saveDataTypeText = 3, "設備存檔\n"
saveDataTypeText = 4, "臨時存檔\n"
saveDataTypeText = 5, "緩存存檔\n"
saveDataTypeText = 6, "系統BCAT存檔\n"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/pt.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

171
romfs/lang/ru.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "NULL"
confirmBlacklist = 0, "Are you sure you want to add #%s# to your blacklist?"
confirmCopy = 0, "Are you sure you want to copy #%s# to #%s#?"
confirmCreateAllSaveData = 0, "Are you sure you would like to create all save data on this system for #%s#? This can take a while depending on how many titles are found."
confirmDelete = 0, "Are you sure you want to delete #%s#? *This is permanent*!"
confirmDeleteBackupsAll = 0, "Are you sure you would like to delete *all* of your save backups for all of your games?"
confirmDeleteBackupsTitle = 0, "Are you sure you would like to delete all save backups for #%s#?"
confirmDeleteSaveData = 0, "*WARNING*: This *will* erase the save data for #%s# *from your system*. Are you sure you want to do this?"
confirmDriveOverwrite = 0, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
confirmOverwrite = 0, "Are you sure you want to overwrite #%s#?"
confirmResetSaveData = 0, "*WARNING*: This *will* reset the save data for this game as if it was never ran before. Are you sure you want to do this?"
confirmRestore = 0, "Are you sure you want to restore #%s#?"
debugStatus = 0, "User Count: "
debugStatus = 1, "Current User: "
debugStatus = 2, "Current Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "Sort Type: "
dialogNo = 0, "No [B]"
dialogOK = 0, "OK [A]"
dialogYes = 0, "Yes [A]"
extrasMenu = 0, "SD to SD Browser"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "Remove Pending Update"
extrasMenu = 6, "Terminate Process"
extrasMenu = 7, "Mount System Save"
extrasMenu = 8, "Rescan Titles"
extrasMenu = 9, "Mount Process RomFS"
extrasMenu = 10, "Backup JKSV Folder"
extrasMenu = 11, "*[DEV]* Output en-US"
fileModeFileProperties = 0, "Path: %s\nSize: %s"
fileModeFolderProperties = 0, "Path: %s\nSub Folders: %u\nFile Count: %u\nTotal Size: %s"
fileModeMenu = 0, "Copy To "
fileModeMenu = 1, "Delete"
fileModeMenu = 2, "Rename"
fileModeMenu = 3, "Make Dir"
fileModeMenu = 4, "Properties"
fileModeMenu = 5, "Close"
fileModeMenu = 6, "Add to Path Filters"
fileModeMenuMkDir = 0, "New"
folderMenuNew = 0, "New Backup"
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
helpSettings = 0, "[A] Toggle [X] Defaults [B] Back"
helpTitle = 0, "[A] Select [L][R] Jump [Y] Favorite [X] Title Options [B] Back"
helpUser = 0, "[A] Select [Y] Dump All Saves [X] User Options"
holdingText = 0, "(Hold) "
holdingText = 1, "(Keep Holding) "
holdingText = 2, "(Almost There!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "Play Time: %02d:%02d"
infoStatus = 3, "Total Launches: %u"
infoStatus = 4, "Publisher: %s"
infoStatus = 5, "Save Type: %s"
infoStatus = 6, "Cache Index: %u"
infoStatus = 7, "User: %s"
infoStatus = 9, ""
loadingStartPage = 0, "Loading..."
mainMenuExtras = 0, "Extras"
mainMenuSettings = 0, "Settings"
onlineErrorConnecting = 0, "Error Connecting!"
onlineNoUpdates = 0, "No Updates Available."
popAddedToPathFilter = 0, "'#%s#' added to path filters."
popCPUBoostEnabled = 0, "CPU Boost Enabled for ZIP."
popChangeOutputError = 0, "#%s# contains illegal or non-ASCII characters."
popChangeOutputFolder = 0, "#%s# changed to #%s#"
popDriveFailed = 0, "Failed to start Google Drive."
popRemoteNotActive = 0, "Remote is not available"
popDriveStarted = 0, "Google Drive started successfully."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "Error committing file to save!"
popFolderIsEmpty = 0, "Folder is empty!"
popProcessShutdown = 0, "#%s# successfully shutdown."
popSVIExported = 0, "SVI Exported."
popSaveIsEmpty = 0, "Save data is empty!"
popTrashEmptied = 0, "Trash emptied"
popZipIsEmpty = 0, "ZIP file is empty!"
saveDataBackupDeleted = 0, "#%s# has been deleted."
saveDataBackupMovedToTrash = 0, "#%s# has been moved to trash."
saveDataCreatedForUser = 0, "Save data created for %s!"
saveDataCreationFailed = 0, "Save data creation failed!"
saveDataDeleteAllUser = 0, "*ARE YOU SURE YOU WANT TO DELETE ALL SAVE DATA FOR %s?*"
saveDataDeleteSuccess = 0, "Save data for #%s# deleted!"
saveDataExtendFailed = 0, "Failed to extend save data."
saveDataExtendSuccess = 0, "Save data for #%s# extended!"
saveDataNoneFound = 0, "No saves found for #%s#!"
saveDataResetSuccess = 0, "Save for #%s# reset!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "Empty Trash Bin"
settingsMenu = 1, "Check for Updates"
settingsMenu = 2, "Set JKSV Save Output Folder"
settingsMenu = 3, "Edit Blacklisted Titles"
settingsMenu = 4, "Delete All Save Backups"
settingsMenu = 5, "Include Device Saves With Users: "
settingsMenu = 6, "Auto Backup On Restore: "
settingsMenu = 7, "Auto-Name Backups: "
settingsMenu = 8, "Overclock/CPU Boost: "
settingsMenu = 9, "Hold To Delete: "
settingsMenu = 10, "Hold To Restore: "
settingsMenu = 11, "Hold To Overwrite: "
settingsMenu = 12, "Force Mount: "
settingsMenu = 13, "Account System Saves: "
settingsMenu = 14, "Enable Writing to System Saves: "
settingsMenu = 15, "Use FS Commands Directly: "
settingsMenu = 16, "Export Saves to ZIP: "
settingsMenu = 17, "Force English To Be Used: "
settingsMenu = 18, "Enable Trash Bin: "
settingsMenu = 19, "Title Sorting Type: "
settingsMenu = 20, "Animation Scale: "
settingsOff = 0, "Off"
settingsOn = 0, ">On>"
sortType = 0, "Alphabetical"
sortType = 1, "Time Played"
sortType = 2, "Last Played"
swkbdEnterName = 0, "Enter a new name"
swkbdExpandSize = 0, "Enter New Size in MB"
swkbdMkDir = 0, "Enter a folder name"
swkbdNewSafeTitle = 0, "Input New Output Folder"
swkbdProcessID = 0, "Enter Process ID"
swkbdRename = 0, "Enter a new name for item"
swkbdSaveIndex = 0, "Enter Cache Index"
swkbdSetWorkDir = 0, "Enter a new Output Path"
swkbdSysSavID = 0, "Enter System Save ID"
threadStatusAddingFileToZip = 0, "Adding '#%s#' to ZIP..."
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
threadStatusCheckingForUpdate = 0, "Checking for updates..."
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
threadStatusCopyingFile = 0, "Copying '#%s#'..."
threadStatusCreatingSaveData = 0, "Creating Save Data for #%s#..."
threadStatusDecompressingFile = 0, "Decompressing '#%s#'..."
threadStatusDeletingFile = 0, "Deleting..."
threadStatusDeletingSaveData = 0, "Deleting Save Data for #%s#..."
threadStatusDeletingUpdate = 0, "Deleting pending update..."
threadStatusDownloadingFile = 0, "Downloading #%s#..."
threadStatusDownloadingUpdate = 0, "Downloading update..."
threadStatusExtendingSaveData = 0, "Extending Save Data for #%s#..."
threadStatusGetDirProps = 0, "Getting Folder Properties..."
threadStatusOpeningFolder = 0, "Opening '#%s#'..."
threadStatusPackingJKSV = 0, "Writing JKSV folder contents to ZIP..."
threadStatusResettingSaveData = 0, "Resetting save data..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "Uploading #%s#..."
titleOptions = 0, "Information"
titleOptions = 1, "Blacklist"
titleOptions = 2, "Change Output Folder"
titleOptions = 3, "Open in File Mode"
titleOptions = 4, "Delete All Save Backups"
titleOptions = 5, "Reset Save Data"
titleOptions = 6, "Delete Save Data"
titleOptions = 7, "Extend Save Data"
titleOptions = 8, "Export SVI"
translationMainPage = 0, "Translation: "
userOptions = 0, "Dump All For "
userOptions = 1, "Create Save Data"
userOptions = 2, "Create All Save Data"
userOptions = 3, "Delete All User Saves"

170
romfs/lang/zh-CN.txt Normal file
View File

@ -0,0 +1,170 @@
author = 0, "NULL"
confirmBlacklist = 0, "是否确定要将#%s#添加到您的黑名单中?"
confirmCopy = 0, "是否确定要将#%s#复制到#%s#"
confirmCreateAllSaveData = 0, "是否确定要在此系统上为#%s#创建所有存档数据这可能需要一些时间具体取决于找到的Title的数量。"
confirmDelete = 0, "是否确定要删除#%s#*这是永久的*"
confirmDeleteBackupsAll = 0, "是否确定要删除您所有游戏的*所有*存档备份?"
confirmDeleteBackupsTitle = 0, "是否确定要删除#%s#的所有存档备份?"
confirmDeleteSaveData = 0, "*警告*:这*将会*从*系统*中删除#%s#的存档数据。您确定要这样做吗?"
confirmDriveOverwrite = 0, "下载这个备份将会覆盖您的SD卡上的备份。是否继续"
confirmOverwrite = 0, "是否确定要覆盖#%s#?"
confirmResetSaveData = 0, "*警告*:这*将会*重置此游戏的存档数据,就像以前从未运行过一样。您确定要这样做吗?"
confirmRestore = 0, "是否确定要还原#%s#"
debugStatus = 0, "用户数量: "
debugStatus = 1, "当前用户: "
debugStatus = 2, "当前Title: "
debugStatus = 3, "安全Title: "
debugStatus = 4, "排序方式: "
dialogNo = 0, "否 [B]"
dialogOK = 0, "确定 [A]"
dialogYes = 0, "是 [A]"
extrasMenu = 0, "文件管理器"
extrasMenu = 1, "BIS分区ProdInfoF"
extrasMenu = 2, "BIS分区Safe"
extrasMenu = 3, "BIS分区System"
extrasMenu = 4, "BIS分区User"
extrasMenu = 5, "删除挂起的更新"
extrasMenu = 6, "终止进程"
extrasMenu = 7, "挂载系统存档"
extrasMenu = 8, "重新扫描Title"
extrasMenu = 9, "挂载进程RomFS"
extrasMenu = 10, "备份JKSV文件夹"
extrasMenu = 11, "*[开发者]* 输出en-US"
fileModeFileProperties = 0, "路径:%s\n大小%s"
fileModeFolderProperties = 0, "路径:%s\n子目录%u\n文件数量%u\n总大小%s"
fileModeMenu = 0, "复制到"
fileModeMenu = 1, "删除"
fileModeMenu = 2, "重命名"
fileModeMenu = 3, "新建文件夹"
fileModeMenu = 4, "属性"
fileModeMenu = 5, "关闭"
fileModeMenu = 6, "添加到路径过滤器"
fileModeMenuMkDir = 0, "新建"
folderMenuNew = 0, "新建备份"
helpFolder = 0, "[A] 选择 [Y] 还原 [X] 删除 [ZR] 上传 [B] 关闭"
helpSettings = 0, "[A] 切换 [X] 恢复默认 [B] 返回"
helpTitle = 0, "[A] 选择 [L][R] 翻页 [Y] 收藏 [X] Title选项 [B] 返回"
helpUser = 0, "[A] 选择 [Y] 转储所有存档 [X] 用户选项"
holdingText = 0, "(按住)"
holdingText = 1, "(继续按住)"
holdingText = 2, "(快好了!)"
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "游玩时间: %02d:%02d"
infoStatus = 3, "启动次数: %u"
infoStatus = 4, "发行商: %s"
infoStatus = 5, "存档类型: %s"
infoStatus = 6, "缓存索引: %u"
infoStatus = 7, "用户: %s"
loadingStartPage = 0, "加载中..."
mainMenuExtras = 0, "附加功能"
mainMenuSettings = 0, "系统设置"
onlineErrorConnecting = 0, "连接错误!"
onlineNoUpdates = 0, "没有可用的更新。"
popAddedToPathFilter = 0, "'#%s#'已添加到路径过滤器。"
popCPUBoostEnabled = 0, "为ZIP压缩启用CPU超频。"
popChangeOutputError = 0, "#%s#包含非法或者非ASCII的字符。"
popChangeOutputFolder = 0, "#%s#更改到#%s#"
popDriveFailed = 0, "Google Drive启动失败。"
popRemoteNotActive = 0, "Remote不可用。"
popDriveStarted = 0, "Google Drive启动成功。"
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "提交要保存的文件时出错!"
popFolderIsEmpty = 0, "文件夹为空!"
popProcessShutdown = 0, "#%s#已成功关闭。"
popSVIExported = 0, "SVI文件已导出"
popSaveIsEmpty = 0, "存档数据为空!"
popTrashEmptied = 0, "回收站已清空"
popZipIsEmpty = 0, "ZIP文件为空"
saveDataBackupDeleted = 0, "#%s#已被删除。"
saveDataBackupMovedToTrash = 0, "#%s#已被移到回收站。"
saveDataCreatedForUser = 0, "%s的存档数据已创建"
saveDataCreationFailed = 0, "存档数据创建失败!"
saveDataDeleteAllUser = 0, "*确定要删除%s的所有存档数据吗*"
saveDataDeleteSuccess = 0, "#%s#的存档数据已删除!"
saveDataExtendFailed = 0, "扩展存档数据失败。"
saveDataExtendSuccess = 0, "#%s#的存档数据已扩展!"
saveDataNoneFound = 0, "没有找到#%s#的存档!"
saveDataResetSuccess = 0, "#%s#的存档已重置!"
saveDataTypeText = 0, "系统存档"
saveDataTypeText = 1, "用户存档"
saveDataTypeText = 2, "BCAT存档"
saveDataTypeText = 3, "设备存档"
saveDataTypeText = 4, "临时存档"
saveDataTypeText = 5, "缓存存档"
saveDataTypeText = 6, "系统BCAT存档"
saveTypeMainMenu = 0, "设备"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "缓存"
saveTypeMainMenu = 3, "系统"
saveTypeMainMenu = 4, "系统BCAT"
saveTypeMainMenu = 5, "临时"
settingsMenu = 0, "清空回收站"
settingsMenu = 1, "检查更新"
settingsMenu = 2, "设置JKSV的存档输出文件夹"
settingsMenu = 3, "编辑Title黑名单"
settingsMenu = 4, "删除所有存档备份"
settingsMenu = 5, "用户存档中包含设备存档:"
settingsMenu = 6, "恢复时自动备份:"
settingsMenu = 7, "自动命名备份:"
settingsMenu = 8, "CPU超频"
settingsMenu = 9, "按住以删除:"
settingsMenu = 10, "按住以恢复:"
settingsMenu = 11, "按住以覆盖:"
settingsMenu = 12, "强制挂载:"
settingsMenu = 13, "用户系统存档:"
settingsMenu = 14, "允许写入系统存档:"
settingsMenu = 15, "直接使用FS命令"
settingsMenu = 16, "导出存档到ZIP文件"
settingsMenu = 17, "强制使用英语:"
settingsMenu = 18, "启用回收站:"
settingsMenu = 19, "Title排序方式"
settingsMenu = 20, "动画比例:"
settingsOff = 0, "关"
settingsOn = 0, ">开>"
sortType = 0, "按软件名"
sortType = 1, "按游玩时间"
sortType = 2, "按最近游玩"
swkbdEnterName = 0, "请输入新的名称"
swkbdExpandSize = 0, "请输入新的大小单位MB"
swkbdMkDir = 0, "请输入文件夹名称"
swkbdNewSafeTitle = 0, "请输入新的输出文件夹"
swkbdProcessID = 0, "请输入进程ID"
swkbdRename = 0, "请输入新的项目名称"
swkbdSaveIndex = 0, "请输入缓存索引"
swkbdSetWorkDir = 0, "请输入新的输出路径"
swkbdSysSavID = 0, "请输入系统存档ID"
threadStatusAddingFileToZip = 0, "正在添加'#%s#'到ZIP文件..."
threadStatusCalculatingSaveSize = 0, "正在计算存档数据大小..."
threadStatusCheckingForUpdate = 0, "正在检查更新..."
threadStatusCompressingSaveForUpload = 0, "正在压缩#%s#以便上传..."
threadStatusCopyingFile = 0, "正在复制'#%s#'..."
threadStatusCreatingSaveData = 0, "正在创建#%s#的存档数据..."
threadStatusDecompressingFile = 0, "正在解压'#%s#'..."
threadStatusDeletingFile = 0, "正在删除..."
threadStatusDeletingSaveData = 0, "正在删除#%s#的存档数据..."
threadStatusDeletingUpdate = 0, "正在删除挂起的更新..."
threadStatusDownloadingFile = 0, "正在下载#%s#..."
threadStatusDownloadingUpdate = 0, "正在下载更新..."
threadStatusExtendingSaveData = 0, "正在扩展#%s#的存档数据..."
threadStatusGetDirProps = 0, "正在获取文件夹属性..."
threadStatusOpeningFolder = 0, "正在打开'#%s#'..."
threadStatusPackingJKSV = 0, "正在写入JKSV文件夹的内容到ZIP文件..."
threadStatusResettingSaveData = 0, "正在重置存档数据..."
threadStatusSavingTranslations = 0, "Saving the file master..."
threadStatusUploadingFile = 0, "正在上传#%s#..."
titleOptions = 0, "信息"
titleOptions = 1, "黑名单"
titleOptions = 2, "更改输出文件夹"
titleOptions = 3, "在文件管理器中打开"
titleOptions = 4, "删除所有存档备份"
titleOptions = 5, "重置存档数据"
titleOptions = 6, "删除存档数据"
titleOptions = 7, "扩展存档数据"
titleOptions = 8, "导出SVI文件"
translationMainPage = 0, "翻译:"
userOptions = 0, "转储所有存档:"
userOptions = 1, "创建存档数据"
userOptions = 2, "创建所有游戏的存档数据"
userOptions = 3, "删除所有用户存档"

171
romfs/lang/zh-TW.txt Normal file
View File

@ -0,0 +1,171 @@
author = 0, "Leo"
confirmBlacklist = 0, "是否確定要將#%s#加入黑名單?"
confirmCopy = 0, "是否確定要將#%s#複製到#%s#?"
confirmCreateAllSaveData = 0, "是否確定要複製系統內所有遊戲進度並新增到使用者#%s#帳號內? 完成此操作的等待時間將取決於系統內的遊戲進度檔案數量."
confirmDelete = 0, "是否確定要刪除#%s#? *此為永久性刪除!*"
confirmDeleteBackupsAll = 0, "是否確定要刪除你備份所有遊戲的 *全部* 存檔進度?"
confirmDeleteBackupsTitle = 0, "是否確定要刪除#%s#的全部存檔進度?"
confirmDeleteSaveData = 0, "*注意*: 此操作 *將會* 完整刪除#%s# *在主機系統內* 的存檔進度. 請再次確認是否要繼續進行?"
confirmDriveOverwrite = 0, "準備將主機的儲存進度備份至記憶卡,將會覆蓋記憶卡上原有的備份檔案. 請再次確認是否要繼續進行?"
confirmOverwrite = 0, "是否確定要覆寫#%s#?"
confirmResetSaveData = 0, "*注意*: 此操作 *將會* 重置歸零此遊戲的存檔進度,像是從未執行過. 請再次確認是否要繼續進行?"
confirmRestore = 0, "是否確定要還原#%s#?"
debugStatus = 0, "使用者數量: "
debugStatus = 1, "目前使用者: "
debugStatus = 2, "目前的Title: "
debugStatus = 3, "Safe Title: "
debugStatus = 4, "排序準則: "
dialogNo = 0, "否 [B]"
dialogOK = 0, "確定 [A]"
dialogYes = 0, "是 [A]"
extrasMenu = 0, "檔案管理視窗"
extrasMenu = 1, "BIS: ProdInfoF"
extrasMenu = 2, "BIS: Safe"
extrasMenu = 3, "BIS: System"
extrasMenu = 4, "BIS: User"
extrasMenu = 5, "移除系統更新通知"
extrasMenu = 6, "終止指定程序"
extrasMenu = 7, "掛載系統存檔"
extrasMenu = 8, "重新掃瞄Titles"
extrasMenu = 9, "掛載RomFS程序"
extrasMenu = 10, "備份JKSV資料夾"
extrasMenu = 11, "*[DEV]* 使用英文介面"
fileModeFileProperties = 0, "路徑: %s\n大小: %s"
fileModeFolderProperties = 0, "路徑: %s\n子目錄: %u\n檔案總數: %u\n檔案容量: %s"
fileModeMenu = 0, "複製到"
fileModeMenu = 1, "刪除"
fileModeMenu = 2, "改檔名"
fileModeMenu = 3, "新建資料夾"
fileModeMenu = 4, "屬性"
fileModeMenu = 5, "關閉"
fileModeMenu = 6, "新增至路徑篩選器"
fileModeMenuMkDir = 0, "新建"
folderMenuNew = 0, "新建目錄"
helpFolder = 0, "[A] 選定 [Y] 還原 [X] 刪除 [ZR] 上傳 [B] 返回"
helpSettings = 0, "[A] 切換 [X] 恢復預設值 [B] 返回"
helpTitle = 0, "[A] 選定 [L][R] 翻頁 [Y] 最愛 [X] Title選項 [B] 返回"
helpUser = 0, "[A] 選定 [X] 使用者選項"
holdingText = 0, "(請按住) "
holdingText = 1, "(繼續按住) "
holdingText = 2, "(確認執行!) "
infoStatus = 0, "TID: %016lX"
infoStatus = 1, "SID: %016lX"
infoStatus = 2, "遊玩時間: %02d:%02d"
infoStatus = 3, "啟動次數: %u"
infoStatus = 4, "發行商: %s"
infoStatus = 5, "存檔類型: %s"
infoStatus = 6, "快取索引: %u"
infoStatus = 7, "使用者: %s"
loadingStartPage = 0, "加載中…"
mainMenuExtras = 0, "附加設定"
mainMenuSettings = 0, "系統設定"
onlineErrorConnecting = 0, "連線失敗!"
onlineNoUpdates = 0, "目前沒有可用的更新版本!"
popAddedToPathFilter = 0, "'#%s#' 已新增至路徑篩選器."
popCPUBoostEnabled = 0, "壓縮ZIP時將啟用CPU超頻."
popChangeOutputError = 0, "#%s# 包含非法或者非ASCII字元."
popChangeOutputFolder = 0, "#%s# 更改到 #%s#"
popDriveFailed = 0, "無法啟用Google雲端硬碟."
popRemoteNotActive = 0, "沒有可用的Remote雲端硬碟."
popDriveStarted = 0, "Google雲端硬碟已正確啟用."
popWebdavStarted = 0, "Webdav started successfully."
popWebdavFailed =, 0, "Failed to start Webdav."
popErrorCommittingFile = 0, "提交檔案進行儲存時發生錯誤!"
popFolderIsEmpty = 0, "資料夾內沒有檔案!"
popProcessShutdown = 0, "#%s# 程序已關閉."
popSVIExported = 0, "已匯出SVI."
popSaveIsEmpty = 0, "沒有找到存檔進度!"
popTrashEmptied = 0, "資源回收筒已清空"
popZipIsEmpty = 0, "ZIP壓縮檔內沒有檔案!"
saveDataBackupDeleted = 0, "#%s#已被刪除."
saveDataBackupMovedToTrash = 0, "#%s#已被移動至資源回收筒."
saveDataCreatedForUser = 0, "存檔已新增至%s!"
saveDataCreationFailed = 0, "存檔新增失敗!"
saveDataDeleteAllUser = 0, "*是否確認要刪除%s使用者的所有遊戲進度存檔?*"
saveDataDeleteSuccess = 0, "#%s#的進度存檔已刪除!"
saveDataExtendFailed = 0, "無法擴充此遊戲的進度儲存空間."
saveDataExtendSuccess = 0, "#%s#的進度存檔儲存空間已擴充!"
saveDataIndexText = 0, "存檔索引:"
saveDataNoneFound = 0, "沒有#%s#的進度存檔!"
saveDataResetSuccess = 0, "#%s#的進度存檔已重置!"
saveDataTypeText = 0, "System"
saveDataTypeText = 1, "Account"
saveDataTypeText = 2, "BCAT"
saveDataTypeText = 3, "Device"
saveDataTypeText = 4, "Temporary"
saveDataTypeText = 5, "Cache"
saveDataTypeText = 6, "System BCAT"
saveTypeMainMenu = 0, "Device"
saveTypeMainMenu = 1, "BCAT"
saveTypeMainMenu = 2, "Cache"
saveTypeMainMenu = 3, "System"
saveTypeMainMenu = 4, "System BCAT"
saveTypeMainMenu = 5, "SysTemp Storagetem"
settingsMenu = 0, "清空資源回收筒"
settingsMenu = 1, "檢查程式更新"
settingsMenu = 2, "設定JKSV進度存檔匯出的資料夾"
settingsMenu = 3, "編輯Titles黑名單"
settingsMenu = 4, "刪除所有進度存檔"
settingsMenu = 5, "包含綁定主機的使用者進度存檔: "
settingsMenu = 6, "還原前自動備份: "
settingsMenu = 7, "備份時自動命名: "
settingsMenu = 8, "CPU超頻: "
settingsMenu = 9, "長按確認後刪除: "
settingsMenu = 10, "長按確認後還原: "
settingsMenu = 11, "長按確認後覆寫: "
settingsMenu = 12, "強制掛載: "
settingsMenu = 13, "使用者系統進度存檔: "
settingsMenu = 14, "允許存取系統進度存檔: "
settingsMenu = 15, "直接使用FS命令: "
settingsMenu = 16, "匯出存檔時壓縮為ZIP: "
settingsMenu = 17, "強制顯示為英文: "
settingsMenu = 18, "啟用資源回收筒: "
settingsMenu = 19, "Title排序模式: "
settingsMenu = 20, "彈出視窗延遲: "
settingsOff = 0, "關"
settingsOn = 0, ">開>"
sortType = 0, "依名稱字母順序"
sortType = 1, "依遊玩時間長度"
sortType = 2, "依最後遊玩時間"
swkbdEnterName = 0, "請輸入新的名稱"
swkbdExpandSize = 0, "輸入新的大小(MB)"
swkbdMkDir = 0, "請輸入資料夾名稱"
swkbdNewSafeTitle = 0, "輸入新的匯出目錄名稱"
swkbdProcessID = 0, "請輸入程序ID"
swkbdRename = 0, "請輸入item的新名稱"
swkbdSaveIndex = 0, "請輸入快取索引"
swkbdSetWorkDir = 0, "請輸入新的匯出路徑"
swkbdSysSavID = 0, "請輸入系統存檔ID"
threadStatusAddingFileToZip = 0, "正在將'#%s#'的存檔壓縮為ZIP..."
threadStatusCalculatingSaveSize = 0, "正在計算進度檔案大小..."
threadStatusCheckingForUpdate = 0, "檢查是否有可用更新..."
threadStatusCompressingSaveForUpload = 0, "正在壓縮#%s#準備上傳..."
threadStatusCopyingFile = 0, "正在複製'#%s#'..."
threadStatusCreatingSaveData = 0, "正在新增#%s#的存檔進度..."
threadStatusDecompressingFile = 0, "'#%s#'存檔正在解壓縮..."
threadStatusDeletingFile = 0, "刪除中..."
threadStatusDeletingSaveData = 0, "正在刪除#%s#的存檔進度.."
threadStatusDeletingUpdate = 0, "正在刪除系統更新..."
threadStatusDownloadingFile = 0, "正在下載#%s#..."
threadStatusDownloadingUpdate = 0, "正在下載更新版本..."
threadStatusExtendingSaveData = 0, "正在擴充#%s#的存檔空間..."
threadStatusGetDirProps = 0, "正在取得資料夾屬性資訊..."
threadStatusOpeningFolder = 0, "正在開啟'#%s#'資料夾..."
threadStatusPackingJKSV = 0, "正在將JKSV資料夾壓縮為ZIP..."
threadStatusResettingSaveData = 0, "正在重置遊戲存檔進度..."
threadStatusSavingTranslations = 0, "正在儲存file master..."
threadStatusUploadingFile = 0, "正在上傳#%s#..."
titleOptions = 0, "詳細資訊"
titleOptions = 1, "設為黑名單"
titleOptions = 2, "變更匯出檔案資料夾"
titleOptions = 3, "以檔案管理器開啟"
titleOptions = 4, "刪除所有進度存檔"
titleOptions = 5, "重置進度存檔"
titleOptions = 6, "刪除進度存檔"
titleOptions = 7, "擴充存檔空間"
titleOptions = 8, "匯出SVI"
translationMainPage = 0, "翻譯:"
userOptions = 0, "備份使用者全部存檔:"
userOptions = 1, "新增遊戲存檔進度"
userOptions = 2, "新增所有遊戲存檔進度"
userOptions = 3, "刪除使用者全部存檔"

561
src/cfg.cpp Normal file
View File

@ -0,0 +1,561 @@
#include <json-c/json.h>
#include <string>
#include <switch.h>
#include <unordered_map>
#include "cfg.h"
#include "data.h"
#include "file.h"
#include "type.h"
#include "ui.h"
#include "util.h"
std::unordered_map<std::string, bool> cfg::config;
std::vector<uint64_t> cfg::blacklist;
std::vector<uint64_t> cfg::favorites;
static std::unordered_map<uint64_t, std::string> pathDefs;
uint8_t cfg::sortType;
std::string cfg::driveClientID, cfg::driveClientSecret, cfg::driveRefreshToken;
std::string cfg::webdavOrigin, cfg::webdavBasePath, cfg::webdavUser, cfg::webdavPassword;
const char *cfgPath = "sdmc:/config/JKSV/JKSV.cfg", *titleDefPath = "sdmc:/config/JKSV/titleDefs.txt",
*workDirLegacy = "sdmc:/switch/jksv_dir.txt";
static std::unordered_map<std::string, unsigned> cfgStrings = {
{"workDir", 0},
{"includeDeviceSaves", 1},
{"autoBackup", 2},
{"overclock", 3},
{"holdToDelete", 4},
{"holdToRestore", 5},
{"holdToOverwrite", 6},
{"forceMount", 7},
{"accountSystemSaves", 8},
{"allowSystemSaveWrite", 9},
{"directFSCommands", 10},
{"exportToZIP", 11},
{"languageOverride", 12},
{"enableTrashBin", 13},
{"titleSortType", 14},
{"animationScale", 15},
{"favorite", 16},
{"blacklist", 17},
{"autoName", 18},
{"driveRefreshToken", 19},
};
const std::string _true_ = "true", _false_ = "false";
bool cfg::isBlacklisted(uint64_t tid)
{
for (uint64_t &bid : cfg::blacklist)
if (tid == bid)
return true;
return false;
}
//Has to be threaded to be compatible with ui::confirm
void cfg::addTitleToBlacklist(void *a)
{
threadInfo *t = (threadInfo *)a;
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
uint64_t tid = d->tid;
cfg::blacklist.push_back(tid);
for (data::user &u : data::users)
{
for (unsigned i = 0; i < u.titleInfo.size(); i++)
if (u.titleInfo[i].tid == tid)
u.titleInfo.erase(u.titleInfo.begin() + i);
}
ui::ttlRefresh();
cfg::saveConfig();
t->finished = true;
}
void cfg::removeTitleFromBlacklist(uint64_t tid)
{
for (unsigned i = 0; i < cfg::blacklist.size(); i++)
{
if (cfg::blacklist[i] == tid)
cfg::blacklist.erase(cfg::blacklist.begin() + i);
}
data::loadUsersTitles(false);
ui::ttlRefresh();
cfg::saveConfig();
}
bool cfg::isFavorite(uint64_t tid)
{
for (uint64_t &fid : cfg::favorites)
if (tid == fid)
return true;
return false;
}
static int getFavoriteIndex(uint64_t tid)
{
for (unsigned i = 0; i < cfg::favorites.size(); i++)
{
if (tid == cfg::favorites[i])
return i;
}
return -1;
}
void cfg::addTitleToFavorites(uint64_t tid)
{
if (cfg::isFavorite(tid))
{
int rem = getFavoriteIndex(tid);
cfg::favorites.erase(cfg::favorites.begin() + rem);
}
else
cfg::favorites.push_back(tid);
data::sortUserTitles();
ui::ttlRefresh();
cfg::saveConfig();
}
bool cfg::isDefined(uint64_t tid)
{
for (auto &def : pathDefs)
if (def.first == tid)
return true;
return false;
}
void cfg::pathDefAdd(uint64_t tid, const std::string &newPath)
{
data::titleInfo *t = data::getTitleInfoByTID(tid);
std::string oldSafe = t->safeTitle;
std::string tmp = util::safeString(newPath);
if (!tmp.empty())
{
pathDefs[tid] = tmp;
t->safeTitle = tmp;
std::string oldOutput = fs::getWorkDir() + oldSafe;
std::string newOutput = fs::getWorkDir() + tmp;
rename(oldOutput.c_str(), newOutput.c_str());
ui::showPopMessage(POP_FRAME_DEFAULT,
ui::getUICString("popChangeOutputFolder", 0),
oldSafe.c_str(),
tmp.c_str());
}
else
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popChangeOutputError", 0), newPath.c_str());
}
std::string cfg::getPathDefinition(uint64_t tid)
{
return pathDefs[tid];
}
void cfg::addPathToFilter(uint64_t tid, const std::string &_p)
{
char outpath[128];
sprintf(outpath, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
FILE *filters = fopen(outpath, "a");
fprintf(filters, "%s\n", _p.c_str());
fclose(filters);
}
void cfg::resetConfig()
{
cfg::config["incDev"] = false;
cfg::config["autoBack"] = true;
cfg::config["ovrClk"] = false;
cfg::config["holdDel"] = true;
cfg::config["holdRest"] = true;
cfg::config["holdOver"] = true;
cfg::config["forceMount"] = true;
cfg::config["accSysSave"] = false;
cfg::config["sysSaveWrite"] = false;
cfg::config["directFsCmd"] = false;
cfg::config["zip"] = true;
cfg::config["langOverride"] = false;
cfg::config["trashBin"] = true;
cfg::config["autoName"] = false;
cfg::sortType = cfg::ALPHA;
ui::animScale = 3.0f;
cfg::config["autoUpload"] = false;
}
static inline bool textToBool(const std::string &_txt)
{
return _txt == _true_ ? true : false;
}
static inline std::string boolToText(bool _b)
{
return _b ? _true_ : _false_;
}
static inline std::string sortTypeText()
{
switch (cfg::sortType)
{
case cfg::ALPHA:
return "ALPHA";
break;
case cfg::MOST_PLAYED:
return "MOST_PLAYED";
break;
case cfg::LAST_PLAYED:
return "LAST_PLAYED";
break;
}
return "";
}
static void loadWorkDirLegacy()
{
if (fs::fileExists(workDirLegacy))
{
char tmp[256];
memset(tmp, 0, 256);
FILE *getDir = fopen(workDirLegacy, "r");
fgets(tmp, 256, getDir);
fclose(getDir);
std::string tmpStr = tmp;
util::stripChar('\n', tmpStr);
util::stripChar('\r', tmpStr);
fs::setWorkDir(tmpStr);
fs::delfile(workDirLegacy);
}
}
static void loadConfigLegacy()
{
std::string legacyCfgPath = fs::getWorkDir() + "cfg.bin";
if (fs::fileExists(legacyCfgPath))
{
FILE *oldCfg = fopen(legacyCfgPath.c_str(), "rb");
uint64_t cfgIn = 0;
fread(&cfgIn, sizeof(uint64_t), 1, oldCfg);
fread(&cfg::sortType, 1, 1, oldCfg);
fread(&ui::animScale, sizeof(float), 1, oldCfg);
if (ui::animScale == 0)
ui::animScale = 3.0f;
fclose(oldCfg);
cfg::config["incDev"] = cfgIn >> 63 & 1;
cfg::config["autoBack"] = cfgIn >> 62 & 1;
cfg::config["ovrClk"] = cfgIn >> 61 & 1;
cfg::config["holdDel"] = cfgIn >> 60 & 1;
cfg::config["holdRest"] = cfgIn >> 59 & 1;
cfg::config["holdOver"] = cfgIn >> 58 & 1;
cfg::config["forceMount"] = cfgIn >> 57 & 1;
cfg::config["accSysSave"] = cfgIn >> 56 & 1;
cfg::config["sysSaveWrite"] = cfgIn >> 55 & 1;
cfg::config["directFsCmd"] = cfgIn >> 53 & 1;
cfg::config["zip"] = cfgIn >> 51 & 1;
cfg::config["langOverride"] = cfgIn >> 50 & 1;
cfg::config["trashBin"] = cfgIn >> 49 & 1;
fs::delfile(legacyCfgPath.c_str());
}
}
static void loadFavoritesLegacy()
{
std::string legacyFavPath = fs::getWorkDir() + "favorites.txt";
if (fs::fileExists(legacyFavPath))
{
fs::dataFile fav(legacyFavPath);
while (fav.readNextLine(false))
cfg::favorites.push_back(strtoul(fav.getLine().c_str(), NULL, 16));
fav.close();
fs::delfile(legacyFavPath);
}
}
static void loadBlacklistLegacy()
{
std::string legacyBlPath = fs::getWorkDir() + "blacklist.txt";
if (fs::fileExists(legacyBlPath))
{
fs::dataFile bl(legacyBlPath);
while (bl.readNextLine(false))
cfg::blacklist.push_back(strtoul(bl.getLine().c_str(), NULL, 16));
bl.close();
fs::delfile(legacyBlPath);
}
}
static void loadTitleDefsLegacy()
{
std::string titleDefLegacy = fs::getWorkDir() + "titleDefs.txt";
if (fs::fileExists(titleDefLegacy))
{
fs::dataFile getPaths(titleDefLegacy);
while (getPaths.readNextLine(true))
{
uint64_t tid = strtoul(getPaths.getName().c_str(), NULL, 16);
pathDefs[tid] = getPaths.getNextValueStr();
}
getPaths.close();
fs::delfile(titleDefLegacy);
}
}
//Oops
static void loadTitleDefs()
{
if (fs::fileExists(titleDefPath))
{
fs::dataFile getPaths(titleDefPath);
while (getPaths.readNextLine(true))
{
uint64_t tid = strtoul(getPaths.getName().c_str(), NULL, 16);
pathDefs[tid] = getPaths.getNextValueStr();
}
}
}
static void loadDriveConfig()
{
// Start Google Drive
fs::dirList cfgList("/config/JKSV/", true);
std::string clientSecretPath;
for (unsigned i = 0; i < cfgList.getCount(); i++)
{
std::string itemName = cfgList.getItem(i);
if (itemName.find("client_secret") != itemName.npos)
{
clientSecretPath = "/config/JKSV/" + cfgList.getItem(i);
break;
}
}
if (!clientSecretPath.empty())
{
json_object *installed, *clientID, *clientSecret, *driveJSON = json_object_from_file(clientSecretPath.c_str());
if (driveJSON)
{
if (json_object_object_get_ex(driveJSON, "installed", &installed))
{
if (json_object_object_get_ex(installed, "client_id", &clientID) &&
json_object_object_get_ex(installed, "client_secret", &clientSecret))
{
cfg::driveClientID = json_object_get_string(clientID);
cfg::driveClientSecret = json_object_get_string(clientSecret);
}
}
json_object_put(driveJSON);
}
}
// End Google Drive
// Webdav
json_object *webdavJSON = json_object_from_file("/config/JKSV/webdav.json");
json_object *origin, *basepath, *username, *password;
if (webdavJSON)
{
if (json_object_object_get_ex(webdavJSON, "origin", &origin))
{
cfg::webdavOrigin = json_object_get_string(origin);
}
if (json_object_object_get_ex(webdavJSON, "basepath", &basepath))
{
cfg::webdavBasePath = json_object_get_string(basepath);
}
if (json_object_object_get_ex(webdavJSON, "username", &username))
{
cfg::webdavUser = json_object_get_string(username);
}
if (json_object_object_get_ex(webdavJSON, "password", &password))
{
cfg::webdavPassword = json_object_get_string(password);
}
json_object_put(webdavJSON);
}
}
void cfg::loadConfig()
{
cfg::resetConfig();
loadWorkDirLegacy();
loadConfigLegacy();
loadFavoritesLegacy();
loadBlacklistLegacy();
loadTitleDefsLegacy();
loadTitleDefs();
if (fs::fileExists(cfgPath))
{
fs::dataFile cfgRead(cfgPath);
while (cfgRead.readNextLine(true))
{
std::string varName = cfgRead.getName();
if (cfgStrings.find(varName) != cfgStrings.end())
{
switch (cfgStrings[varName])
{
case 0:
fs::setWorkDir(cfgRead.getNextValueStr());
break;
case 1:
cfg::config["incDev"] = textToBool(cfgRead.getNextValueStr());
break;
case 2:
cfg::config["autoBack"] = textToBool(cfgRead.getNextValueStr());
break;
case 3:
cfg::config["ovrClk"] = textToBool(cfgRead.getNextValueStr());
break;
case 4:
cfg::config["holdDel"] = textToBool(cfgRead.getNextValueStr());
break;
case 5:
cfg::config["holdRest"] = textToBool(cfgRead.getNextValueStr());
break;
case 6:
cfg::config["holdOver"] = textToBool(cfgRead.getNextValueStr());
break;
case 7:
cfg::config["forceMount"] = textToBool(cfgRead.getNextValueStr());
break;
case 8:
cfg::config["accSysSave"] = textToBool(cfgRead.getNextValueStr());
break;
case 9:
cfg::config["sysSaveWrite"] = textToBool(cfgRead.getNextValueStr());
break;
case 10:
cfg::config["directFsCmd"] = textToBool(cfgRead.getNextValueStr());
break;
case 11:
cfg::config["zip"] = textToBool(cfgRead.getNextValueStr());
break;
case 12:
cfg::config["langOverride"] = textToBool(cfgRead.getNextValueStr());
break;
case 13:
cfg::config["trashBin"] = textToBool(cfgRead.getNextValueStr());
break;
case 14:
{
std::string getSort = cfgRead.getNextValueStr();
if (getSort == "ALPHA")
cfg::sortType = cfg::ALPHA;
else if (getSort == "MOST_PLAYED")
cfg::sortType = cfg::MOST_PLAYED;
else
cfg::sortType = cfg::LAST_PLAYED;
}
break;
case 15:
{
std::string animFloat = cfgRead.getNextValueStr();
ui::animScale = strtof(animFloat.c_str(), NULL);
}
break;
case 16:
{
std::string tid = cfgRead.getNextValueStr();
cfg::favorites.push_back(strtoul(tid.c_str(), NULL, 16));
}
break;
case 17:
{
std::string tid = cfgRead.getNextValueStr();
cfg::blacklist.push_back(strtoul(tid.c_str(), NULL, 16));
}
break;
case 18:
cfg::config["autoName"] = textToBool(cfgRead.getNextValueStr());
break;
case 19:
cfg::driveRefreshToken = cfgRead.getNextValueStr();
break;
case 20:
cfg::config["autoUpload"] = textToBool(cfgRead.getNextValueStr());
break;
default:
break;
}
}
}
}
loadDriveConfig();
}
static void savePathDefs()
{
FILE *pathOut = fopen(titleDefPath, "w");
for (auto &p : pathDefs)
fprintf(pathOut, "0x%016lX = \"%s\"\n", p.first, p.second.c_str());
fclose(pathOut);
}
void cfg::saveConfig()
{
FILE *cfgOut = fopen("sdmc:/config/JKSV/JKSV.cfg", "w");
fprintf(cfgOut, "#JKSV config.\nworkDir = \"%s\"\n\n", fs::getWorkDir().c_str());
fprintf(cfgOut, "includeDeviceSaves = %s\n", boolToText(cfg::config["incDev"]).c_str());
fprintf(cfgOut, "autoBackup = %s\n", boolToText(cfg::config["autoBack"]).c_str());
fprintf(cfgOut, "autoName = %s\n", boolToText(cfg::config["autoName"]).c_str());
fprintf(cfgOut, "overclock = %s\n", boolToText(cfg::config["ovrClk"]).c_str());
fprintf(cfgOut, "holdToDelete = %s\n", boolToText(cfg::config["holdDel"]).c_str());
fprintf(cfgOut, "holdToRestore = %s\n", boolToText(cfg::config["holdRest"]).c_str());
fprintf(cfgOut, "holdToOverwrite = %s\n", boolToText(cfg::config["holdOver"]).c_str());
fprintf(cfgOut, "forceMount = %s\n", boolToText(cfg::config["forceMount"]).c_str());
fprintf(cfgOut, "accountSystemSaves = %s\n", boolToText(cfg::config["accSysSaves"]).c_str());
fprintf(cfgOut, "allowSystemSaveWrite = %s\n", boolToText(cfg::config["sysSaveWrite"]).c_str());
fprintf(cfgOut, "directFSCommands = %s\n", boolToText(cfg::config["directFsCmd"]).c_str());
fprintf(cfgOut, "exportToZIP = %s\n", boolToText(cfg::config["zip"]).c_str());
fprintf(cfgOut, "languageOverride = %s\n", boolToText(cfg::config["langOverride"]).c_str());
fprintf(cfgOut, "enableTrashBin = %s\n", boolToText(cfg::config["trashBin"]).c_str());
fprintf(cfgOut, "titleSortType = %s\n", sortTypeText().c_str());
fprintf(cfgOut, "animationScale = %f\n", ui::animScale);
if (!cfg::driveRefreshToken.empty())
fprintf(cfgOut, "driveRefreshToken = %s\n", cfg::driveRefreshToken.c_str());
if (!cfg::favorites.empty())
{
fprintf(cfgOut, "\n#favorites\n");
for (uint64_t &f : cfg::favorites)
fprintf(cfgOut, "favorite = 0x%016lX\n", f);
}
if (!cfg::blacklist.empty())
{
fprintf(cfgOut, "\n#blacklist\n");
for (uint64_t &b : cfg::blacklist)
fprintf(cfgOut, "blacklist = 0x%016lX\n", b);
}
fclose(cfgOut);
if (!pathDefs.empty())
savePathDefs();
}

101
src/curlfuncs.cpp Normal file
View File

@ -0,0 +1,101 @@
#include <string>
#include <vector>
#include <curl/curl.h>
#include "curlfuncs.h"
#include "util.h"
size_t curlFuncs::writeDataString(const char *buff, size_t sz, size_t cnt, void *u)
{
std::string *str = (std::string *)u;
str->append(buff, 0, sz * cnt);
return sz * cnt;
}
size_t curlFuncs::writeHeaders(const char *buff, size_t sz, size_t cnt, void *u)
{
std::vector<std::string> *headers = (std::vector<std::string> *)u;
headers->push_back(buff);
return sz * cnt;
}
size_t curlFuncs::readDataFile(char *buff, size_t sz, size_t cnt, void *u)
{
curlFuncs::curlUpArgs*in = (curlFuncs::curlUpArgs *)u;
size_t ret = fread(buff, sz, cnt, in->f);
if(in->o)
*in->o = ftell(in->f);
return ret;
}
std::string curlFuncs::getHeader(const std::string& name, std::vector<std::string> *h)
{
std::string ret = HEADER_ERROR;
for (unsigned i = 0; i < h->size(); i++)
{
std::string curHeader = h->at(i);
size_t colonPos = curHeader.find_first_of(':');
if (curHeader.substr(0, colonPos) == name)
{
ret = curHeader.substr(colonPos + 2);
break;
}
}
util::stripChar('\n', ret);
util::stripChar('\r', ret);
return ret;
}
std::string curlFuncs::getJSONURL(std::vector<std::string> *headers, const std::string& _url)
{
std::string ret;
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, _url.c_str());
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(handle, CURLOPT_USERAGENT, "JKSV");
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeDataString);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &ret);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 15);
if(headers)
{
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, writeHeaders);
curl_easy_setopt(handle, CURLOPT_HEADERDATA, headers);
}
if(curl_easy_perform(handle) != CURLE_OK)
ret.clear();//JIC
curl_easy_cleanup(handle);
return ret;
}
size_t writeDataBin(uint8_t *buff, size_t sz, size_t cnt, void *u)
{
size_t sizeIn = sz * cnt;
std::vector<uint8_t> *binData = (std::vector<uint8_t> *)u;
binData->reserve(sizeIn);
binData->insert(binData->end(), buff, buff + sizeIn);
return sizeIn;
}
bool curlFuncs::getBinURL(std::vector<uint8_t> *out, const std::string& _url)
{
bool ret = false;
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, _url.c_str());
curl_easy_setopt(handle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(handle, CURLOPT_USERAGENT, "JKSV");
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeDataBin);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, out);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 15);
if(curl_easy_perform(handle) == CURLE_OK)
ret = true;
curl_easy_cleanup(handle);
return ret;
}

View File

@ -1,317 +1,649 @@
#include <vector>
#include <string>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <memory>
#include <string>
#include <string_view>
#include <switch.h>
#include <unordered_map>
#include <vector>
#include "cfg.h"
#include "data.h"
#include "file.h"
#include "type.h"
#include "util.h"
//Sorts titles sort-of alphabetically
namespace
{
/// @brief This is the string used for when a publisher isn't found in the NACP.
constexpr std::string_view STRING_UNKOWN_AUTHOR = "Someone?";
/// @brief This array holds the order in which to scan save data space IDs since the all pseudo value from libnx doesn't seem to work.
constexpr std::array<FsSaveDataSpaceId, 7> ORDER_SAVE_DATA_SPACES = {FsSaveDataSpaceId_System,
FsSaveDataSpaceId_User,
FsSaveDataSpaceId_SdSystem,
FsSaveDataSpaceId_Temporary,
FsSaveDataSpaceId_SdUser,
FsSaveDataSpaceId_ProperSystem,
FsSaveDataSpaceId_SafeMode};
/// @brief This saves whether or not the System BCAT user was pushed.
bool s_systemBCATPushed = false;
/// @brief This saves whether or not the Temporary save user was pushed.
bool s_temporaryPushed = false;
} // namespace
/// @brief External variables I used to track selected user/title.
int selUser = 0, selData = 0;
/// @brief This vector holds the user instances.
std::vector<data::user> data::users;
/// @brief System language. I don't know why this is here in data?
SetLanguage data::sysLang;
/// @brief This is the map of titleInfo.
std::unordered_map<uint64_t, data::titleInfo> data::titles;
//Sorts titles by sortType
static struct
{
bool operator()(data::titledata& a, data::titledata& b)
{
uint32_t tmpA, tmpB;
for(unsigned i = 0; i < a.getTitle().length(); )
bool operator()(const data::userTitleInfo &a, const data::userTitleInfo &b)
{
ssize_t uCnt = decode_utf8(&tmpA, (const uint8_t *)&a.getTitle().data()[i]);
decode_utf8(&tmpB, (const uint8_t *)&b.getTitle().data()[i]);
tmpA = tolower(tmpA);
tmpB = tolower(tmpB);
if(tmpA != tmpB)
return tmpA < tmpB;
//Favorites override EVERYTHING
if (cfg::isFavorite(a.tid) != cfg::isFavorite(b.tid))
return cfg::isFavorite(a.tid);
i += uCnt;
switch (cfg::sortType)
{
case cfg::ALPHA:
{
std::string titleA = data::getTitleNameByTID(a.tid);
std::string titleB = data::getTitleNameByTID(b.tid);
uint32_t pointA, pointB;
for (unsigned i = 0, j = 0; i < titleA.length();)
{
ssize_t aCnt = decode_utf8(&pointA, (const uint8_t *)&titleA.data()[i]);
ssize_t bCnt = decode_utf8(&pointB, (const uint8_t *)&titleB.data()[j]);
pointA = tolower(pointA), pointB = tolower(pointB);
if (pointA != pointB)
return pointA < pointB;
i += aCnt;
j += bCnt;
}
}
break;
case cfg::MOST_PLAYED:
return a.playStats.playtime > b.playStats.playtime;
break;
case cfg::LAST_PLAYED:
return a.playStats.last_timestamp_user > b.playStats.last_timestamp_user;
break;
}
return false;
}
return false;
}
} sortTitles;
//Returns -1 for new
static int getUserIndex(const u128& id)
static int getUserIndex(AccountUid id)
{
for(unsigned i = 0; i < data::users.size(); i++)
{
if(data::users[i].getUID() == id)
u128 nId = util::accountUIDToU128(id);
for (unsigned i = 0; i < data::users.size(); i++)
if (data::users[i].getUID128() == nId)
return i;
}
return -1;
}
namespace data
static inline bool accountSystemSaveCheck(const FsSaveDataInfo &_inf)
{
titledata curData;
user curUser;
std::vector<icn> icons;
std::vector<user> users;
bool sysSave = false;
void loadDataInfo()
{
icn defIcon;
defIcon.load(0, "romfs:/img/icn/icnDefault.png");
icons.push_back(defIcon);
for(unsigned i = 0; i < users.size(); i++)
users[i].titles.clear();
users.clear();
Result res = 0;
FsSaveDataIterator saveIt;
size_t total = 0;
FsSaveDataInfo info;
res = fsOpenSaveDataIterator(&saveIt, FsSaveDataSpaceId_All);
if(R_FAILED(res))
{
printf("SaveDataIterator Failed\n");
return;
}
while(true)
{
res = fsSaveDataIteratorRead(&saveIt, &info, 1, &total);
if(R_FAILED(res) || total == 0)
break;
if((info.SaveDataType == FsSaveDataType_SaveData) || sysSave)
{
int u = getUserIndex(info.userID);
if(u == -1)
{
user newUser;
if(newUser.init(info.userID) || (sysSave && newUser.initNoChk(info.userID)))
{
users.push_back(newUser);
u = getUserIndex(info.userID);
titledata newData;
if(newData.init(info) && newData.isMountable(newUser.getUID()))
{
users[u].titles.push_back(newData);
}
}
}
else
{
titledata newData;
if(newData.init(info) && newData.isMountable(users[u].getUID()))
{
users[u].titles.push_back(newData);
}
}
}
}
fsSaveDataIteratorClose(&saveIt);
for(unsigned i = 0; i < users.size(); i++)
std::sort(users[i].titles.begin(), users[i].titles.end(), sortTitles);
curUser = users[0];
}
void exit()
{
for(unsigned i = 0; i < users.size(); i++)
texDestroy(users[i].userIcon);
for(unsigned i = 0; i < icons.size(); i++)
icons[i].deleteData();
}
void icn::load(const uint64_t& _id, const uint8_t *jpegData, const size_t& jpegSize)
{
titleID = _id;
iconTex = texLoadJPEGMem(jpegData, jpegSize);
}
void icn::load(const uint64_t& _id, const std::string& _png)
{
titleID = _id;
iconTex = texLoadPNGFile(_png.c_str());
}
int findIcnIndex(const uint64_t& titleID)
{
for(unsigned i = 0; i < icons.size(); i++)
{
if(icons[i].getTitleID() == titleID)
return i;
}
return -1;
}
bool titledata::init(const FsSaveDataInfo& inf)
{
Result res = 0;
NsApplicationControlData *dat = new NsApplicationControlData;
std::memset(dat, 0, sizeof(NsApplicationControlData));
NacpLanguageEntry *ent = NULL;
if(inf.SaveDataType == FsSaveDataType_SaveData)
id = inf.titleID;
else if(inf.SaveDataType == FsSaveDataType_SystemSaveData)
id = inf.saveID;
uID = inf.userID;
type = (FsSaveDataType)inf.SaveDataType;
size_t outSz = 0;
res = nsGetApplicationControlData(1, id, dat, sizeof(NsApplicationControlData), &outSz);
if(R_FAILED(res) || outSz < sizeof(dat->nacp))
{
if(!sysSave)
printf("nsGetAppCtrlData Failed: 0x%08X\n", (unsigned)res);
delete dat;
}
if(R_SUCCEEDED(res))
{
res = nacpGetLanguageEntry(&dat->nacp, &ent);
if(R_FAILED(res) || ent == NULL)
{
printf("nacpGetLanguageEntry Failed\n");
delete dat;
}
}
if(R_SUCCEEDED(res))
{
title.assign(ent->name);
titleSafe = util::safeString(ent->name);
if(titleSafe.empty())
{
char tmp[18];
sprintf(tmp, "%016lX", id);
titleSafe.assign(tmp);
}
int iconIndex = findIcnIndex(id);
if(iconIndex == -1)
{
uint32_t icnSize = outSz - sizeof(dat->nacp);
icn newIcon;
newIcon.load(id, dat->icon, icnSize);
icons.push_back(newIcon);
icon = icons[findIcnIndex(id)];
}
else
icon = icons[iconIndex];
delete dat;
}
else
{
char tmp[32];
sprintf(tmp, "%016lX", (u64)id);
title.assign(tmp);
titleSafe = util::safeString(tmp);
icon = icons[0];
}
return true;
}
bool titledata::isMountable(const u128& uID)
{
data::user tmpUser;
tmpUser.initNoChk(uID);
if(fs::mountSave(tmpUser, *this))
{
fsdevUnmountDevice("sv");
return true;
}
if (_inf.save_data_type == FsSaveDataType_System && util::accountUIDToU128(_inf.uid) != 0 &&
!cfg::config["accSysSave"])
return false;
}
//ASCII Testing
void titledata::debugCreate(const uint64_t& _id, const std::string& t)
{
id = _id;
title = t;
titleSafe = util::safeString(t);
if(titleSafe.empty())
{
char tmp[18];
sprintf(tmp, "%016lX", id);
titleSafe.assign(tmp);
}
}
bool user::init(const u128& _id)
{
Result res = 0;
userID = _id;
AccountProfile prof;
AccountProfileBase base;
res = accountGetProfile(&prof, userID);
if(R_FAILED(res))
return false;
res = accountProfileGet(&prof, NULL, &base);
if(R_FAILED(res))
return false;
username.assign(base.username);
if(username.empty())
username = "Unknown";
userSafe = util::safeString(username);
size_t sz = 0;
accountProfileGetImageSize(&prof, &sz);
uint8_t *profJpeg = new uint8_t[sz];
accountProfileLoadImage(&prof, profJpeg, sz, &sz);
userIcon = texLoadJPEGMem(profJpeg, sz);
delete[] profJpeg;
accountProfileClose(&prof);
return true;
}
//Minimal init/test to avoid loading and creating things I don't need
static bool testMount(const FsSaveDataInfo &_inf)
{
bool ret = false;
if (!cfg::config["forceMount"])
return true;
}
bool user::initNoChk(const u128& _id)
if ((ret = fs::mountSave(_inf)))
fs::unmountSave();
return ret;
}
/// @brief This version of the function uses the title/application ID to pull the data from the system.
/// @param titleID Title/Application ID of the title to add.
static void add_title_to_list(uint64_t titleID)
{
// This is the output size of the control data.
uint64_t dataSize = 0;
// This is the size of the icon.
size_t iconSize = 0;
// Nacp language entry pointer.
NacpLanguageEntry *entry = nullptr;
// Try to read the control data directly into the map.
Result nsError = nsGetApplicationControlData(NsApplicationControlSource_Storage,
titleID,
&data::titles[titleID].data,
sizeof(NsApplicationControlData),
&dataSize);
// At this point, regardless, the title should exist in the map. Using a reference from here on out.
data::titleInfo &info = data::titles[titleID];
// Calc icon size. Not sure this is really needed?
iconSize = dataSize - sizeof(NacpStruct);
// If that failed, run the backup routine and bail.
if (R_FAILED(nsError) || dataSize < sizeof(info.data.nacp) || iconSize <= 0 ||
R_FAILED(nacpGetLanguageEntry(&info.data.nacp, &entry)))
{
Result res = 0;
userID = _id;
// Clear the NACP.
std::memset(&info.data.nacp, 0x00, sizeof(NacpStruct));
AccountProfile prof;
AccountProfileBase base;
// Set the title and publisher.
info.title = util::getIDStr(titleID);
info.author = STRING_UNKOWN_AUTHOR;
res = accountGetProfile(&prof, userID);
if(R_SUCCEEDED(res))
// Check if the title has a path defined in the config.
if (cfg::isDefined(titleID))
{
res = accountProfileGet(&prof, NULL, &base);
}
if(R_SUCCEEDED(res))
{
username.assign(base.username);
userSafe = util::safeString(username);
accountProfileClose(&prof);
info.safeTitle = cfg::getPathDefinition(titleID);
}
else
{
username = "Unknown";
userSafe = "Unknown";
//This shouldn't happen too much
userIcon = texLoadPNGFile("romfs:/img/icn/icnDefault.png");
info.safeTitle = util::getIDStr(titleID);
}
return true;
// Create the icon.
std::string idHex = util::getIDStrLower(titleID);
info.icon = util::createIconGeneric(idHex.c_str(), 32, true);
// Bail. Do not continue. We're done.
return;
}
else
{
// Check to make sure the title isn't blank. If it is, use English.
if (std::char_traits<char>::length(entry->name) == 0)
{
info.title = info.data.nacp.lang[SetLanguage_ENUS].name;
}
else
{
info.title = entry->name;
}
// Check the same for author/publisher.
if (std::char_traits<char>::length(entry->author) == 0)
{
info.author = info.data.nacp.lang[SetLanguage_ENUS].author;
}
else
{
info.author = entry->author;
}
// Load the icon from the control data.
info.icon = gfx::texMgr->textureLoadFromMem(IMG_FMT_JPG, info.data.icon, iconSize);
// Make sure to save that the title has valid control data.
info.hasControlData = true;
}
// The following is universal whether the title has control data or not.
// Set the safe path title. Note: I hate the second else if statement, but screw it. Rewrite avoids this kind of stuff.
if (cfg::isDefined(titleID))
{
info.safeTitle = cfg::getPathDefinition(titleID);
}
else if ((info.safeTitle = util::safeString(entry->name)) == "")
{
info.safeTitle = util::getIDStr(titleID);
}
// Favorite check.
if (cfg::isFavorite(titleID))
{
info.fav = true;
}
}
/// @brief Adds a title to the map using the application ID and cached control data.
/// @param titleID Application ID of the title.
/// @param data Reference to the data to use.
static void add_title_to_list(uint64_t titleID, NsApplicationControlData &data)
{
// Start by memcpy'ing the data over.
std::memcpy(&data::titles[titleID].data, &data, sizeof(NsApplicationControlData));
// Since this function is always being fed cached data, we're going to assume everything else is good.
}
static inline bool titleIsLoaded(uint64_t titleID)
{
return data::titles.find(titleID) != data::titles.end();
}
static void loadUserAccounts(void)
{
// Total accounts listed.
int total = 0;
// Switch has eight accounts max.
AccountUid accounts[8] = {0};
if (R_FAILED(accountListAllUsers(accounts, 8, &total)))
{
// Rewrite logs this.
return;
}
for (int i = 0; i < total; i++)
{
// Maybe to do: This looks stupid. Overload constructors?
data::users.emplace_back(accounts[i], "", "");
}
}
//This can load titles installed without having save data
static void loadTitlesFromRecords()
{
NsApplicationRecord nsRecord;
int32_t entryCount = 0, recordOffset = 0;
while (R_SUCCEEDED(nsListApplicationRecord(&nsRecord, 1, recordOffset++, &entryCount)) && entryCount > 0)
{
if (!titleIsLoaded(nsRecord.application_id))
{
add_title_to_list(nsRecord.application_id);
}
}
}
static void import_svi_files(void)
{
// This is the path used. JKSV master was written before fslib.
std::string sviPath = fs::getWorkDir() + "svi";
// Get the listing and check to make sure there was actually something in it.
fs::dirList sviList(sviPath);
if (sviList.getCount() <= 0)
{
// Just return and don't do anything.
return;
}
// Loop through the listing and load them all.
for (unsigned int i = 0; i < sviList.getCount(); i++)
{
// Full path to the file.
std::string fullPath = sviPath + "/" + sviList.getItem(i);
// Grab the size of the SVI before continuing.
size_t sviSize = fs::fsize(fullPath);
// Application ID
uint64_t applicationID = 0;
// Language entry.
NacpLanguageEntry *entry = nullptr;
// Icon size.
size_t iconSize = 0;
// Open the file and read the application ID.
std::FILE *sviFile = std::fopen(sviPath.c_str(), "rb");
std::fread(&applicationID, 1, sizeof(uint64_t), sviFile);
// If it already exists in the map, just continue.
if (titleIsLoaded(applicationID))
{
std::fclose(sviFile);
continue;
}
// Read the NACP data into the map directly.
std::fread(&data::titles[applicationID].data.nacp, 1, sizeof(NacpStruct), sviFile);
// Calculate the icon size and read that too. The ControlData has memory set aside for the icon anyway.
iconSize = fs::fsize(fullPath) - (sizeof(uint64_t) + sizeof(NacpStruct));
std::fread(&data::titles[applicationID].data.icon, 1, iconSize, sviFile);
// Think I should be safe to use a reference now?
data::titleInfo &info = data::titles[applicationID];
// Setup the title stuff.
if (R_FAILED(nacpGetLanguageEntry(&info.data.nacp, &entry)))
{
// Default to English.
info.title = info.data.nacp.lang[SetLanguage_ENUS].name;
info.author = info.data.nacp.lang[SetLanguage_ENUS].author;
}
else
{
info.title = entry->name;
info.author = entry->author;
}
// Safe path.
if (cfg::isDefined(applicationID))
{
info.safeTitle = cfg::getPathDefinition(applicationID);
}
else if ((info.safeTitle = util::safeString(info.title)).empty())
{
info.safeTitle = util::getIDStr(applicationID);
}
// Favorite
if (cfg::isFavorite(applicationID))
{
info.fav = true;
}
// Just going to assume this is good.
info.icon = gfx::texMgr->textureLoadFromMem(IMG_FMT_JPG, info.data.icon, iconSize);
}
}
bool data::loadUsersTitles(bool clearUsers)
{
static unsigned systemUserCount = 4;
FsSaveDataInfoReader it;
FsSaveDataInfo info;
s64 total = 0;
loadTitlesFromRecords();
import_svi_files();
//Clear titles
for (data::user &u : data::users)
{
u.titleInfo.clear();
}
if (clearUsers)
{
systemUserCount = 4;
for (data::user &u : data::users)
{
u.delIcon();
}
data::users.clear();
loadUserAccounts();
s_systemBCATPushed = false;
s_temporaryPushed = false;
users.emplace_back(util::u128ToAccountUID(3), ui::getUIString("saveTypeMainMenu", 0), "Device");
users.emplace_back(util::u128ToAccountUID(2), ui::getUIString("saveTypeMainMenu", 1), "BCAT");
users.emplace_back(util::u128ToAccountUID(5), ui::getUIString("saveTypeMainMenu", 2), "Cache");
users.emplace_back(util::u128ToAccountUID(0), ui::getUIString("saveTypeMainMenu", 3), "System");
}
for (unsigned i = 0; i < 7; i++)
{
if (R_FAILED(fsOpenSaveDataInfoReader(&it, ORDER_SAVE_DATA_SPACES.at(i))))
continue;
while (R_SUCCEEDED(fsSaveDataInfoReaderRead(&it, &info, 1, &total)) && total != 0)
{
uint64_t tid = 0;
if (info.save_data_type == FsSaveDataType_System || info.save_data_type == FsSaveDataType_SystemBcat)
tid = info.system_save_data_id;
else
tid = info.application_id;
if (!titleIsLoaded(tid))
{
add_title_to_list(tid);
}
//Don't bother with this stuff
if (cfg::isBlacklisted(tid) || !accountSystemSaveCheck(info) || !testMount(info))
continue;
switch (info.save_data_type)
{
case FsSaveDataType_Bcat:
info.uid = util::u128ToAccountUID(2);
break;
case FsSaveDataType_Device:
info.uid = util::u128ToAccountUID(3);
break;
case FsSaveDataType_SystemBcat:
info.uid = util::u128ToAccountUID(4);
if (!s_systemBCATPushed)
{
++systemUserCount;
s_systemBCATPushed = true;
users.emplace_back(util::u128ToAccountUID(4),
ui::getUIString("saveTypeMainMenu", 4),
"System BCAT");
}
break;
case FsSaveDataType_Cache:
info.uid = util::u128ToAccountUID(5);
break;
case FsSaveDataType_Temporary:
info.uid = util::u128ToAccountUID(6);
if (!s_temporaryPushed)
{
++systemUserCount;
s_temporaryPushed = true;
users.emplace_back(util::u128ToAccountUID(6),
ui::getUIString("saveTypeMainMenu", 5),
"Temporary");
}
break;
}
int u = getUserIndex(info.uid);
if (u == -1)
{
users.emplace(data::users.end() - systemUserCount, info.uid, "", "");
u = getUserIndex(info.uid);
}
PdmPlayStatistics playStats;
if (info.save_data_type == FsSaveDataType_Account || info.save_data_type == FsSaveDataType_Device)
pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(info.application_id,
info.uid,
false,
&playStats);
else
memset(&playStats, 0, sizeof(PdmPlayStatistics));
users[u].addUserTitleInfo(tid, &info, &playStats);
}
fsSaveDataInfoReaderClose(&it);
}
if (cfg::config["incDev"])
{
//Get reference to device save user
unsigned devPos = getUserIndex(util::u128ToAccountUID(3));
data::user &dev = data::users[devPos];
for (unsigned i = 0; i < devPos; i++)
{
//Not needed but makes this easier to read
data::user &u = data::users[i];
u.titleInfo.insert(u.titleInfo.end(), dev.titleInfo.begin(), dev.titleInfo.end());
}
}
data::sortUserTitles();
return true;
}
void data::sortUserTitles()
{
for (data::user &u : data::users)
std::sort(u.titleInfo.begin(), u.titleInfo.end(), sortTitles);
}
void data::init()
{
data::loadUsersTitles(true);
}
void data::exit()
{
/*Still needed for planned future revisions*/
}
void data::setUserIndex(unsigned _sUser)
{
selUser = _sUser;
}
data::user *data::getCurrentUser()
{
return &users[selUser];
}
unsigned data::getCurrentUserIndex()
{
return selUser;
}
void data::setTitleIndex(unsigned _sTitle)
{
selData = _sTitle;
}
data::userTitleInfo *data::getCurrentUserTitleInfo()
{
return &users[selUser].titleInfo[selData];
}
unsigned data::getCurrentUserTitleInfoIndex()
{
return selData;
}
data::titleInfo *data::getTitleInfoByTID(const uint64_t &tid)
{
if (titles.find(tid) != titles.end())
return &titles[tid];
return NULL;
}
std::string data::getTitleNameByTID(const uint64_t &tid)
{
return titles[tid].title;
}
std::string data::getTitleSafeNameByTID(const uint64_t &tid)
{
return titles[tid].safeTitle;
}
SDL_Texture *data::getTitleIconByTID(const uint64_t &tid)
{
return titles[tid].icon;
}
int data::getTitleIndexInUser(const data::user &u, const uint64_t &tid)
{
for (unsigned i = 0; i < u.titleInfo.size(); i++)
{
if (u.titleInfo[i].tid == tid)
return i;
}
return -1;
}
data::user::user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName)
{
userID = _id;
uID128 = util::accountUIDToU128(_id);
AccountProfile prof;
AccountProfileBase base;
if (R_SUCCEEDED(accountGetProfile(&prof, userID)) && R_SUCCEEDED(accountProfileGet(&prof, NULL, &base)))
{
username = base.nickname;
userSafe = util::safeString(username);
if (userSafe.empty())
{
char tmp[32];
sprintf(tmp, "Acc%08X", (uint32_t)uID128);
userSafe = tmp;
}
uint32_t jpgSize = 0;
accountProfileGetImageSize(&prof, &jpgSize);
uint8_t *jpegData = new uint8_t[jpgSize];
accountProfileLoadImage(&prof, jpegData, jpgSize, &jpgSize);
userIcon = gfx::texMgr->textureLoadFromMem(IMG_FMT_JPG, jpegData, jpgSize);
delete[] jpegData;
accountProfileClose(&prof);
}
else
{
username = _backupName.empty() ? util::getIDStr((uint64_t)uID128) : _backupName;
userSafe = _safeBackupName.empty() ? util::getIDStr((uint64_t)uID128) : _safeBackupName;
userIcon = util::createIconGeneric(_backupName.c_str(), 48, false);
}
titles.reserve(64);
}
data::user::user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName, SDL_Texture *img)
: user(_id, _backupName, _safeBackupName)
{
delIcon();
userIcon = img;
titles.reserve(64);
}
void data::user::setUID(AccountUid _id)
{
userID = _id;
uID128 = util::accountUIDToU128(_id);
}
void data::user::addUserTitleInfo(const uint64_t &tid, const FsSaveDataInfo *_saveInfo, const PdmPlayStatistics *_stats)
{
data::userTitleInfo newInfo;
newInfo.tid = tid;
memcpy(&newInfo.saveInfo, _saveInfo, sizeof(FsSaveDataInfo));
memcpy(&newInfo.playStats, _stats, sizeof(PdmPlayStatistics));
titleInfo.push_back(newInfo);
}
static constexpr SDL_Color green = {0x00, 0xDD, 0x00, 0xFF};
void data::dispStats()
{
data::user *cu = data::getCurrentUser();
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
//Easiest/laziest way to do this
std::string stats = ui::getUICString("debugStatus", 0) + std::to_string(users.size()) + "\n";
for (data::user &u : data::users)
stats += u.getUsername() + ": " + std::to_string(u.titleInfo.size()) + "\n";
stats += ui::getUICString("debugStatus", 1) + cu->getUsername() + "\n";
stats += ui::getUICString("debugStatus", 2) + data::getTitleNameByTID(d->tid) + "\n";
stats += ui::getUICString("debugStatus", 3) + data::getTitleSafeNameByTID(d->tid) + "\n";
stats += ui::getUICString("debugStatus", 4) + std::to_string(cfg::sortType) + "\n";
gfx::drawTextf(NULL, 16, 2, 2, &green, stats.c_str());
}

View File

@ -1,326 +0,0 @@
#include <fstream>
#include <cstdio>
#include <switch.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include "file.h"
#include "util.h"
#include "ui.h"
#include "gfx.h"
#include "data.h"
#define BUFF_SIZE 512 * 1024
static std::string wd;
namespace fs
{
void init()
{
mkdir("sdmc:/JKSV", 777);
chdir("sdmc:/JKSV");
wd = "sdmc:/JKSV/";
}
bool mountSave(data::user& usr, data::titledata& open)
{
FsFileSystem sv;
Result res = 0;
if(open.getType() == FsSaveDataType_SaveData)
{
res = fsMount_SaveData(&sv, open.getID(), usr.getUID());
if(R_FAILED(res))
return false;
int r = fsdevMountDevice("sv", sv);
if(r == -1)
return false;
}
else if(data::sysSave)
{
res = fsMount_SystemSaveData(&sv, open.getID());
if(R_FAILED(res))
return false;
int r = fsdevMountDevice("sv", sv);
if(r == -1)
return false;
}
return true;
}
dirList::dirList(const std::string& _path)
{
path = _path;
d = opendir(path.c_str());
while((ent = readdir(d)))
{
item.push_back(ent->d_name);
}
closedir(d);
}
void dirList::reassign(const std::string& _path)
{
path = _path;
d = opendir(path.c_str());
item.clear();
while((ent = readdir(d)))
{
item.push_back(ent->d_name);
}
closedir(d);
}
void dirList::rescan()
{
item.clear();
d = opendir(path.c_str());
while((ent = readdir(d)))
{
item.push_back(ent->d_name);
}
closedir(d);
}
std::string dirList::getItem(int index)
{
return item[index];
}
bool dirList::isDir(int index)
{
std::string fullPath = path + item[index];
struct stat s;
if(stat(fullPath.c_str(), &s) == 0)
{
if(S_ISDIR(s.st_mode))
return true;
}
return false;
}
unsigned dirList::getCount()
{
return item.size();
}
void copyFile(const std::string& from, const std::string& to)
{
std::fstream f(from, std::ios::in | std::ios::binary);
std::fstream t(to, std::ios::out | std::ios::binary);
f.seekg(0, f.end);
size_t fileSize = f.tellg();
f.seekg(0, f.beg);
uint8_t *buff = new uint8_t[BUFF_SIZE];
ui::progBar prog(fileSize);
std::string copyString = "Copying " + from + "...";
copyString = util::getWrappedString(copyString, 24, 1136);
for(unsigned i = 0; i < fileSize; )
{
f.read((char *)buff, BUFF_SIZE);
t.write((char *)buff, f.gcount());
i += f.gcount();
prog.update(i);
prog.draw(copyString);
gfxHandleBuffs();
}
delete[] buff;
f.close();
t.close();
}
void copyFileCommit(const std::string& from, const std::string& to, const std::string& dev)
{
std::fstream f(from, std::ios::in | std::ios::binary);
std::fstream t(to, std::ios::out | std::ios::binary);
f.seekg(0, f.end);
size_t fileSize = f.tellg();
f.seekg(0, f.beg);
uint8_t *buff = new uint8_t[BUFF_SIZE];
ui::progBar prog(fileSize);
std::string copyString = "Copying " + from + "...";
copyString = util::getWrappedString(copyString, 24, 1136);
for(unsigned i = 0; i < fileSize; )
{
f.read((char *)buff, BUFF_SIZE);
t.write((char *)buff, f.gcount());
i += f.gcount();
prog.update(i);
prog.draw(copyString);
gfxHandleBuffs();
}
delete[] buff;
f.close();
t.close();
Result res = fsdevCommitDevice(dev.c_str());
if(R_FAILED(res))
ui::showError("Error committing file to device", res);
}
void copyDirToDir(const std::string& from, const std::string& to)
{
dirList list(from);
for(unsigned i = 0; i < list.getCount(); i++)
{
if(list.isDir(i))
{
std::string newFrom = from + list.getItem(i) + "/";
std::string newTo = to + list.getItem(i);
mkdir(newTo.c_str(), 0777);
newTo += "/";
copyDirToDir(newFrom, newTo);
}
else
{
std::string fullFrom = from + list.getItem(i);
std::string fullTo = to + list.getItem(i);
copyFile(fullFrom, fullTo);
}
}
}
void copyDirToDirCommit(const std::string& from, const std::string& to, const std::string& dev)
{
dirList list(from);
for(unsigned i = 0; i < list.getCount(); i++)
{
if(list.isDir(i))
{
std::string newFrom = from + list.getItem(i) + "/";
std::string newTo = to + list.getItem(i);
mkdir(newTo.c_str(), 0777);
newTo += "/";
copyDirToDirCommit(newFrom, newTo, dev);
}
else
{
std::string fullFrom = from + list.getItem(i);
std::string fullTo = to + list.getItem(i);
copyFileCommit(fullFrom, fullTo, dev);
}
}
}
void delDir(const std::string& path)
{
dirList list(path);
for(unsigned i = 0; i < list.getCount(); i++)
{
if(list.isDir(i))
{
std::string newPath = path + "/" + list.getItem(i) + "/";
delDir(newPath);
std::string delPath = path + list.getItem(i);
rmdir(delPath.c_str());
}
else
{
std::string delPath = path + list.getItem(i);
std::remove(delPath.c_str());
}
}
rmdir(path.c_str());
}
void dumpAllUserSaves(data::user& u)
{
for(unsigned i = 0; i < u.titles.size(); i++)
{
if(fs::mountSave(u, u.titles[i]))
{
util::makeTitleDir(u, u.titles[i]);
//sdmc:/JKSV/[title]/[user] - [date]/
std::string outPath = util::getTitleDir(u, u.titles[i]) + u.getUsernameSafe() + " - " + util::getDateTime();
mkdir(outPath.c_str(), 777);
outPath += "/";
std::string root = "sv:/";
fs::copyDirToDir(root, outPath);
fsdevUnmountDevice("sv");
}
}
}
std::string getFileProps(const std::string& _path)
{
std::string ret = "";
std::fstream get(_path, std::ios::in | std::ios::binary);
if(get.is_open())
{
//Size
get.seekg(0, get.end);
unsigned fileSize = get.tellg();
get.seekg(0, get.beg);
get.close();
//Probably add more later
char tmp[256];
std::sprintf(tmp, "Path: \"%s\"\nSize: %u", _path.c_str(), fileSize);
ret = tmp;
}
return ret;
}
bool fileExists(const std::string& path)
{
std::fstream chk(path, std::ios::in);
if(chk.is_open())
{
chk.close();
return true;
}
return false;
}
std::string getWorkDir()
{
return wd;
}
}

730
src/fs.cpp Normal file
View File

@ -0,0 +1,730 @@
#include <string>
#include <switch.h>
#include "cfg.h"
#include "fs.h"
#include "util.h"
static std::string wd = "sdmc:/JKSV/";
static FSFILE *debLog;
static FsFileSystem sv;
static std::vector<std::string> pathFilter;
void fs::init()
{
mkDirRec("sdmc:/config/JKSV/");
mkDirRec(wd);
mkdir(std::string(wd + "_TRASH_").c_str(), 777);
fs::logOpen();
}
bool fs::mountSave(const FsSaveDataInfo &_m)
{
Result svOpen;
FsSaveDataAttribute attr = {0};
switch (_m.save_data_type)
{
case FsSaveDataType_System:
case FsSaveDataType_SystemBcat:
{
attr.uid = _m.uid;
attr.system_save_data_id = _m.system_save_data_id;
attr.save_data_type = _m.save_data_type;
svOpen = fsOpenSaveDataFileSystemBySystemSaveDataId(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Account:
{
attr.uid = _m.uid;
attr.application_id = _m.application_id;
attr.save_data_type = _m.save_data_type;
attr.save_data_rank = _m.save_data_rank;
attr.save_data_index = _m.save_data_index;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Device:
{
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Device;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Bcat:
{
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Bcat;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Cache:
{
attr.application_id = _m.application_id;
attr.save_data_type = FsSaveDataType_Cache;
attr.save_data_index = _m.save_data_index;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
case FsSaveDataType_Temporary:
{
attr.application_id = _m.application_id;
attr.save_data_type = _m.save_data_type;
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId)_m.save_data_space_id, &attr);
}
break;
default:
svOpen = 1;
break;
}
return R_SUCCEEDED(svOpen) && fsdevMountDevice("sv", sv) != -1;
}
bool fs::commitToDevice(const std::string &dev)
{
bool ret = true;
Result res = fsdevCommitDevice(dev.c_str());
if (R_FAILED(res))
{
fs::logWrite("Error committing file to device -> 0x%X\n", res);
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popErrorCommittingFile", 0));
ret = false;
}
return ret;
}
std::string fs::getWorkDir()
{
return wd;
}
void fs::setWorkDir(const std::string &_w)
{
wd = _w;
}
void fs::loadPathFilters(uint64_t tid)
{
char path[256];
sprintf(path, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
if (fs::fileExists(path))
{
fs::dataFile filter(path);
while (filter.readNextLine(false))
pathFilter.push_back(filter.getLine());
}
}
bool fs::pathIsFiltered(const std::string &_path)
{
if (pathFilter.empty())
return false;
for (std::string &_p : pathFilter)
{
if (_path == _p)
return true;
}
return false;
}
void fs::freePathFilters()
{
pathFilter.clear();
}
void fs::createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t)
{
data::titleInfo *tinfo = data::getTitleInfoByTID(_tid);
if (t)
t->status->setStatus(ui::getUICString("threadStatusCreatingSaveData", 0), tinfo->title.c_str());
uint16_t cacheIndex = 0;
std::string indexStr;
if (_type == FsSaveDataType_Cache &&
!(indexStr = util::getStringInput(SwkbdType_NumPad, "0", ui::getUIString("swkbdSaveIndex", 0), 2, 0, NULL))
.empty())
cacheIndex = strtoul(indexStr.c_str(), NULL, 10);
else if (_type == FsSaveDataType_Cache && indexStr.empty())
{
if (t)
t->finished = true;
return;
}
FsSaveDataAttribute attr;
memset(&attr, 0, sizeof(FsSaveDataAttribute));
attr.application_id = _tid;
attr.uid = _uid;
attr.system_save_data_id = 0;
attr.save_data_type = _type;
attr.save_data_rank = 0;
attr.save_data_index = cacheIndex;
FsSaveDataCreationInfo crt;
memset(&crt, 0, sizeof(FsSaveDataCreationInfo));
int64_t saveSize = 0, journalSize = 0;
// Grab a pointer to the nacp of the title data.
NacpStruct *nacp = &tinfo->data.nacp;
switch (_type)
{
case FsSaveDataType_Account:
{
saveSize = nacp->user_account_save_data_size;
journalSize = nacp->user_account_save_data_journal_size;
}
break;
case FsSaveDataType_Device:
{
saveSize = nacp->device_save_data_size;
journalSize = nacp->device_save_data_journal_size;
}
break;
case FsSaveDataType_Bcat:
{
saveSize = nacp->bcat_delivery_cache_storage_size;
journalSize = nacp->bcat_delivery_cache_storage_size;
}
break;
case FsSaveDataType_Cache:
{
saveSize = 32 * 1024 * 1024;
if (nacp->cache_storage_journal_size > nacp->cache_storage_data_and_journal_size_max)
{
journalSize = nacp->cache_storage_journal_size;
}
else
{
journalSize = nacp->cache_storage_data_and_journal_size_max;
}
}
break;
default:
{
if (t)
t->finished = true;
return;
}
break;
}
crt.save_data_size = saveSize;
crt.journal_size = journalSize;
crt.available_size = 0x4000;
crt.owner_id = _type == FsSaveDataType_Bcat ? 0x010000000000000C : nacp->save_data_owner_id;
crt.flags = 0;
crt.save_data_space_id = FsSaveDataSpaceId_User;
FsSaveDataMetaInfo meta;
memset(&meta, 0, sizeof(FsSaveDataMetaInfo));
if (_type != FsSaveDataType_Bcat)
{
meta.size = 0x40060;
meta.type = FsSaveDataMetaType_Thumbnail;
}
Result res = 0;
if (R_SUCCEEDED(res = fsCreateSaveDataFileSystem(&attr, &crt, &meta)))
{
util::createTitleDirectoryByTID(_tid);
data::loadUsersTitles(false);
ui::ttlRefresh();
}
else
{
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataCreationFailed", 0));
fs::logWrite("SaveCreate Failed -> %X\n", res);
}
}
static void createSaveData_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::svCreateArgs *crt = (fs::svCreateArgs *)t->argPtr;
fs::createSaveData(crt->type, crt->tid, crt->account, t);
delete crt;
t->finished = true;
}
void fs::createSaveDataThreaded(FsSaveDataType _type, uint64_t _tid, AccountUid _uid)
{
fs::svCreateArgs *send = new fs::svCreateArgs;
send->type = _type;
send->tid = _tid;
send->account = _uid;
ui::newThread(createSaveData_t, send, NULL);
}
bool fs::extendSaveData(const data::userTitleInfo *tinfo, uint64_t extSize, threadInfo *t)
{
if (t)
t->status->setStatus(ui::getUICString("threadStatusExtendingSaveData", 0),
data::getTitleNameByTID(tinfo->tid).c_str());
uint64_t journal = fs::getJournalSizeMax(tinfo);
uint64_t saveID = tinfo->saveInfo.save_data_id;
FsSaveDataSpaceId space = (FsSaveDataSpaceId)tinfo->saveInfo.save_data_space_id;
Result res = 0;
if (R_FAILED((res = fsExtendSaveDataFileSystem(space, saveID, extSize, journal))))
{
int64_t totalSize = 0;
fs::mountSave(tinfo->saveInfo);
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &totalSize);
fs::unmountSave();
fs::logWrite("Extend Failed: %uMB to %uMB -> %X\n", totalSize / 1024 / 1024, extSize / 1024 / 1024, res);
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataExtendFailed", 0));
return false;
}
return true;
}
static void extendSaveData_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::svExtendArgs *e = (fs::svExtendArgs *)t->argPtr;
fs::extendSaveData(e->tinfo, e->extSize, t);
delete e;
t->finished = true;
}
void fs::extendSaveDataThreaded(const data::userTitleInfo *tinfo, uint64_t extSize)
{
fs::svExtendArgs *send = new fs::svExtendArgs;
send->tinfo = tinfo;
send->extSize = extSize;
ui::newThread(extendSaveData_t, send, NULL);
}
uint64_t fs::getJournalSize(const data::userTitleInfo *tinfo)
{
uint64_t ret = 0;
data::titleInfo *t = data::getTitleInfoByTID(tinfo->tid);
// NACP pointer.
NacpStruct *nacp = &t->data.nacp;
switch (tinfo->saveInfo.save_data_type)
{
case FsSaveDataType_Account:
{
ret = nacp->user_account_save_data_journal_size;
}
break;
case FsSaveDataType_Device:
{
ret = nacp->device_save_data_journal_size;
}
break;
case FsSaveDataType_Bcat:
{
ret = nacp->bcat_delivery_cache_storage_size;
}
break;
case FsSaveDataType_Cache:
{
if (nacp->cache_storage_journal_size > 0)
{
ret = nacp->cache_storage_journal_size;
}
else
{
ret = nacp->cache_storage_data_and_journal_size_max;
}
}
break;
default:
{
ret = BUFF_SIZE;
}
break;
}
return ret;
}
uint64_t fs::getJournalSizeMax(const data::userTitleInfo *tinfo)
{
uint64_t ret = 0;
data::titleInfo *extend = data::getTitleInfoByTID(tinfo->tid);
// NACP pointer
NacpStruct *nacp = &extend->data.nacp;
switch (tinfo->saveInfo.save_data_type)
{
case FsSaveDataType_Account:
{
if (nacp->user_account_save_data_journal_size_max > nacp->user_account_save_data_journal_size)
ret = nacp->user_account_save_data_journal_size_max;
else
ret = nacp->user_account_save_data_journal_size;
}
break;
case FsSaveDataType_Bcat:
{
ret = nacp->bcat_delivery_cache_storage_size;
}
break;
case FsSaveDataType_Cache:
{
if (nacp->cache_storage_data_and_journal_size_max > nacp->cache_storage_journal_size)
ret = nacp->cache_storage_data_and_journal_size_max;
else
ret = nacp->cache_storage_journal_size;
}
break;
case FsSaveDataType_Device:
{
if (nacp->device_save_data_journal_size_max > nacp->device_save_data_journal_size)
ret = nacp->device_save_data_journal_size_max;
else
ret = nacp->device_save_data_journal_size;
}
break;
default:
{
//will just fail
ret = 0;
}
break;
}
return ret;
}
static void wipeSave_t(void *a)
{
threadInfo *t = (threadInfo *)a;
t->status->setStatus(ui::getUICString("threadStatusResettingSaveData", 0));
fs::delDir("sv:/");
fs::commitToDevice("sv");
t->finished = true;
}
void fs::wipeSave()
{
ui::newThread(wipeSave_t, NULL, NULL);
}
void fs::createNewBackup(void *a)
{
if (!fs::dirNotEmpty("sv:/"))
{
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSaveIsEmpty", 0));
return;
}
uint64_t held = ui::padKeysHeld();
data::user *u = data::getCurrentUser();
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
data::titleInfo *t = data::getTitleInfoByTID(d->tid);
std::string out;
if (held & HidNpadButton_R || cfg::config["autoName"])
out = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD);
else if (held & HidNpadButton_L)
out = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YDM);
else if (held & HidNpadButton_ZL)
out = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_HOYSTE);
else
{
const std::string dict[] = {util::getDateTime(util::DATE_FMT_YMD),
util::getDateTime(util::DATE_FMT_YDM),
util::getDateTime(util::DATE_FMT_HOYSTE),
util::getDateTime(util::DATE_FMT_JHK),
util::getDateTime(util::DATE_FMT_ASC),
u->getUsernameSafe(),
t->safeTitle,
util::generateAbbrev(d->tid),
".zip"};
std::string defaultText = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD);
out = util::getStringInput(SwkbdType_QWERTY, defaultText, ui::getUIString("swkbdEnterName", 0), 64, 9, dict);
out = util::safeString(out);
}
if (!out.empty())
{
std::string ext = util::getExtensionFromString(out);
std::string path = util::generatePathByTID(d->tid) + out;
if (cfg::config["zip"] || ext == "zip")
{
if (ext != "zip") //data::zip is on but extension is not zip
path += ".zip";
zipFile zip = zipOpen64(path.c_str(), 0);
fs::copyDirToZipThreaded("sv:/", zip, false, 0);
}
else
{
fs::mkDir(path);
path += "/";
fs::copyDirToDirThreaded("sv:/", path);
}
ui::fldRefreshMenu();
}
}
void fs::overwriteBackup(void *a)
{
threadInfo *t = (threadInfo *)a;
std::string *dst = (std::string *)t->argPtr;
bool saveHasFiles = fs::dirNotEmpty("sv:/");
if (fs::isDir(*dst) && saveHasFiles)
{
fs::delDir(*dst);
fs::mkDir(*dst);
dst->append("/");
fs::copyDirToDirThreaded("sv:/", *dst);
}
else if (!fs::isDir(*dst) && util::getExtensionFromString(*dst) == "zip" && saveHasFiles)
{
fs::delfile(*dst);
zipFile zip = zipOpen64(dst->c_str(), 0);
fs::copyDirToZipThreaded("sv:/", zip, false, 0);
}
delete dst;
t->finished = true;
}
void fs::restoreBackup(void *a)
{
threadInfo *t = (threadInfo *)a;
std::string *restore = (std::string *)t->argPtr;
data::user *u = data::getCurrentUser();
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
if ((utinfo->saveInfo.save_data_type != FsSaveDataType_System || cfg::config["sysSaveWrite"]))
{
bool saveHasFiles = fs::dirNotEmpty("sv:/");
if (cfg::config["autoBack"] && cfg::config["zip"] && saveHasFiles)
{
std::string autoZip = util::generatePathByTID(utinfo->tid) + "/AUTO " + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + ".zip";
zipFile zip = zipOpen64(autoZip.c_str(), 0);
fs::copyDirToZipThreaded("sv:/", zip, false, 0);
}
else if (cfg::config["autoBack"] && saveHasFiles)
{
std::string autoFolder = util::generatePathByTID(utinfo->tid) + "/AUTO - " + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(autoFolder.substr(0, autoFolder.length() - 1));
fs::copyDirToDirThreaded("sv:/", autoFolder);
}
if (fs::isDir(*restore))
{
restore->append("/");
if (fs::dirNotEmpty(*restore))
{
t->status->setStatus(ui::getUICString("threadStatusCalculatingSaveSize", 0));
unsigned dirCount = 0, fileCount = 0;
uint64_t saveSize = 0;
int64_t availSize = 0;
fs::getDirProps(*restore, dirCount, fileCount, saveSize);
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
if ((int)saveSize > availSize)
{
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
fs::unmountSave();
fs::extendSaveData(utinfo, saveSize + 0x500000, t);
fs::mountSave(utinfo->saveInfo);
}
fs::wipeSave();
fs::copyDirToDirCommitThreaded(*restore, "sv:/", "sv");
}
else
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popFolderIsEmpty", 0));
}
else if (!fs::isDir(*restore) && util::getExtensionFromString(*restore) == "zip")
{
unzFile unz = unzOpen64(restore->c_str());
if (unz && fs::zipNotEmpty(unz))
{
t->status->setStatus(ui::getUICString("threadStatusCalculatingSaveSize", 0));
uint64_t saveSize = fs::getZipTotalSize(unz);
int64_t availSize = 0;
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
if ((int)saveSize > availSize)
{
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
fs::unmountSave();
fs::extendSaveData(utinfo, saveSize + 0x500000, t);
fs::mountSave(utinfo->saveInfo);
}
fs::wipeSave();
fs::copyZipToDirThreaded(unz, "sv:/", "sv");
}
else
{
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popZipIsEmpty", 0));
unzClose(unz);
}
}
else
{
std::string dstPath = "sv:/" + util::getFilenameFromPath(*restore);
fs::copyFileCommitThreaded(*restore, dstPath, "sv");
}
}
if (cfg::config["autoBack"])
ui::fldRefreshMenu();
delete restore;
t->finished = true;
}
void fs::deleteBackup(void *a)
{
threadInfo *t = (threadInfo *)a;
std::string *deletePath = (std::string *)t->argPtr;
std::string backupName = util::getFilenameFromPath(*deletePath);
t->status->setStatus(ui::getUICString("threadStatusDeletingFile", 0));
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
if (cfg::config["trashBin"])
{
std::string oldPath = *deletePath;
std::string trashPath = wd + "_TRASH_/" + data::getTitleSafeNameByTID(utinfo->tid);
fs::mkDir(trashPath);
trashPath += "/" + backupName;
rename(oldPath.c_str(), trashPath.c_str());
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupMovedToTrash", 0), backupName.c_str());
}
else if (fs::isDir(*deletePath))
{
*deletePath += "/";
fs::delDir(*deletePath);
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
}
else
{
fs::delfile(*deletePath);
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
}
ui::fldRefreshMenu();
delete deletePath;
t->finished = true;
}
void fs::dumpAllUserSaves(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
t->argPtr = c;
data::user *u = data::getCurrentUser();
for (unsigned i = 0; i < u->titleInfo.size(); i++)
{
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
if (saveMounted && fs::dirNotEmpty("sv:/") && cfg::config["zip"])
{
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + ".zip";
zipFile zip = zipOpen64(dst.c_str(), 0);
fs::copyDirToZip("sv:/", zip, false, 0, t);
zipClose(zip, NULL);
fs::freePathFilters();
}
else if (saveMounted && fs::dirNotEmpty("sv:/"))
{
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(dst.substr(0, dst.length() - 1));
fs::copyDirToDir("sv:/", dst, t);
fs::freePathFilters();
}
fs::unmountSave();
}
fs::copyArgsDestroy(c);
t->finished = true;
}
void fs::dumpAllUsersAllSaves(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
t->argPtr = c;
unsigned curUser = 0;
while (data::users[curUser].getUID128() != 2)
{
data::user *u = &data::users[curUser++];
for (unsigned i = 0; i < u->titleInfo.size(); i++)
{
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
if (saveMounted && fs::dirNotEmpty("sv:/") && cfg::config["zip"])
{
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + ".zip";
zipFile zip = zipOpen64(dst.c_str(), 0);
fs::copyDirToZip("sv:/", zip, false, 0, t);
zipClose(zip, NULL);
fs::freePathFilters();
}
else if (saveMounted && fs::dirNotEmpty("sv:/"))
{
fs::loadPathFilters(u->titleInfo[i].tid);
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
util::getDateTime(util::DATE_FMT_YMD) + "/";
fs::mkDir(dst.substr(0, dst.length() - 1));
fs::copyDirToDir("sv:/", dst, t);
fs::freePathFilters();
}
fs::unmountSave();
}
}
fs::copyArgsDestroy(c);
t->finished = true;
}
void fs::logOpen()
{
std::string logPath = wd + "log.txt";
debLog = fsfopen(logPath.c_str(), FsOpenMode_Write);
fsfclose(debLog);
}
void fs::logWrite(const char *fmt, ...)
{
std::string logPath = wd + "log.txt";
debLog = fsfopen(logPath.c_str(), FsOpenMode_Append | FsOpenMode_Write);
char tmp[256];
va_list args;
va_start(args, fmt);
vsprintf(tmp, fmt, args);
va_end(args);
fsfwrite(tmp, 1, strlen(tmp), debLog);
fsfclose(debLog);
}

270
src/fs/dir.cpp Normal file
View File

@ -0,0 +1,270 @@
#include <switch.h>
#include <algorithm>
#include "fs.h"
#include "cfg.h"
#include "util.h"
static struct
{
bool operator()(const fs::dirItem& a, const fs::dirItem& b)
{
if(a.isDir() != b.isDir())
return a.isDir();
for(unsigned i = 0; i < a.getItm().length(); i++)
{
char charA = tolower(a.getItm()[i]);
char charB = tolower(b.getItm()[i]);
if(charA != charB)
return charA < charB;
}
return false;
}
} sortDirList;
void fs::mkDir(const std::string& _p)
{
if(cfg::config["directFsCmd"])
fsMkDir(_p.c_str());
else
mkdir(_p.c_str(), 777);
}
void fs::mkDirRec(const std::string& _p)
{
//skip first slash
size_t pos = _p.find('/', 0) + 1;
while((pos = _p.find('/', pos)) != _p.npos)
{
fs::mkDir(_p.substr(0, pos).c_str());
++pos;
}
}
void fs::delDir(const std::string& path)
{
dirList list(path);
for(unsigned i = 0; i < list.getCount(); i++)
{
if(pathIsFiltered(path + list.getItem(i)))
continue;
if(list.isDir(i))
{
std::string newPath = path + list.getItem(i) + "/";
delDir(newPath);
std::string delPath = path + list.getItem(i);
rmdir(delPath.c_str());
}
else
{
std::string delPath = path + list.getItem(i);
std::remove(delPath.c_str());
}
}
rmdir(path.c_str());
}
bool fs::dirNotEmpty(const std::string& _dir)
{
fs::dirList tmp(_dir);
return tmp.getCount() > 0;
}
bool fs::isDir(const std::string& _path)
{
struct stat s;
return stat(_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode);
}
void fs::copyDirToDir(const std::string& src, const std::string& dst, threadInfo *t)
{
if(t)
t->status->setStatus(ui::getUICString("threadStatusOpeningFolder", 0), src.c_str());
fs::dirList *list = new fs::dirList(src);
for(unsigned i = 0; i < list->getCount(); i++)
{
if(pathIsFiltered(src + list->getItem(i)))
continue;
if(list->isDir(i))
{
std::string newSrc = src + list->getItem(i) + "/";
std::string newDst = dst + list->getItem(i) + "/";
fs::mkDir(newDst.substr(0, newDst.length() - 1));
fs::copyDirToDir(newSrc, newDst, t);
}
else
{
std::string fullSrc = src + list->getItem(i);
std::string fullDst = dst + list->getItem(i);
if(t)
t->status->setStatus(ui::getUICString("threadStatusCopyingFile", 0), fullSrc.c_str());
fs::copyFile(fullSrc, fullDst, t);
}
}
delete list;
}
static void copyDirToDir_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
fs::copyDirToDir(in->src, in->dst, t);
if(in->cleanup)
fs::copyArgsDestroy(in);
t->finished = true;
}
void fs::copyDirToDirThreaded(const std::string& src, const std::string& dst)
{
fs::copyArgs *send = fs::copyArgsCreate(src, dst, "", NULL, NULL, true, false, 0);
ui::newThread(copyDirToDir_t, send, fs::fileDrawFunc);
}
void fs::copyDirToDirCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t)
{
if(t)
t->status->setStatus(ui::getUICString("threadStatusOpeningFolder", 0), src.c_str());
fs::dirList *list = new fs::dirList(src);
for(unsigned i = 0; i < list->getCount(); i++)
{
if(pathIsFiltered(src + list->getItem(i)))
continue;
if(list->isDir(i))
{
std::string newSrc = src + list->getItem(i) + "/";
std::string newDst = dst + list->getItem(i) + "/";
fs::mkDir(newDst.substr(0, newDst.length() - 1));
fs::copyDirToDirCommit(newSrc, newDst, dev, t);
}
else
{
std::string fullSrc = src + list->getItem(i);
std::string fullDst = dst + list->getItem(i);
if(t)
t->status->setStatus(ui::getUICString("threadStatusCopyingFile", 0), fullSrc.c_str());
fs::copyFileCommit(fullSrc, fullDst, dev, t);
}
}
delete list;
}
static void copyDirToDirCommit_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
fs::copyDirToDirCommit(in->src, in->dst, in->dev, t);
if(in->cleanup)
fs::copyArgsDestroy(in);
t->finished = true;
}
void fs::copyDirToDirCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev)
{
fs::copyArgs *send = fs::copyArgsCreate(src, dst, dev, NULL, NULL, true, false, 0);
ui::newThread(copyDirToDirCommit_t, send, fs::fileDrawFunc);
}
void fs::getDirProps(const std::string& path, unsigned& dirCount, unsigned& fileCount, uint64_t& totalSize)
{
fs::dirList *d = new fs::dirList(path);
for(unsigned i = 0; i < d->getCount(); i++)
{
if(d->isDir(i))
{
++dirCount;
std::string newPath = path + d->getItem(i) + "/";
fs::getDirProps(newPath, dirCount, fileCount, totalSize);
}
else
{
++fileCount;
std::string filePath = path + d->getItem(i);
totalSize += fs::fsize(filePath);
}
}
delete d;
}
fs::dirItem::dirItem(const std::string& pathTo, const std::string& sItem)
{
itm = sItem;
std::string fullPath = pathTo + sItem;
struct stat s;
if(stat(fullPath.c_str(), &s) == 0 && S_ISDIR(s.st_mode))
dir = true;
}
std::string fs::dirItem::getName() const
{
size_t extPos = itm.find_last_of('.'), slPos = itm.find_last_of('/');
if(extPos == itm.npos)
return "";
return itm.substr(slPos + 1, extPos);
}
std::string fs::dirItem::getExt() const
{
return util::getExtensionFromString(itm);
}
fs::dirList::dirList(const std::string& _path, bool ignoreDotFiles)
{
DIR *d;
struct dirent *ent;
path = _path;
d = opendir(path.c_str());
while((ent = readdir(d)))
if (!ignoreDotFiles || ent->d_name[0] != '.')
item.emplace_back(path, ent->d_name);
closedir(d);
std::sort(item.begin(), item.end(), sortDirList);
}
void fs::dirList::reassign(const std::string& _path)
{
DIR *d;
struct dirent *ent;
path = _path;
d = opendir(path.c_str());
item.clear();
while((ent = readdir(d)))
item.emplace_back(path, ent->d_name);
closedir(d);
std::sort(item.begin(), item.end(), sortDirList);
}
void fs::dirList::rescan()
{
item.clear();
DIR *d;
struct dirent *ent;
d = opendir(path.c_str());
while((ent = readdir(d)))
item.emplace_back(path, ent->d_name);
closedir(d);
std::sort(item.begin(), item.end(), sortDirList);
}

395
src/fs/file.cpp Normal file
View File

@ -0,0 +1,395 @@
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <switch.h>
#include <unistd.h>
#include <cstdarg>
#include <mutex>
#include <condition_variable>
#include <sys/stat.h>
#include "fs.h"
#include "util.h"
#include "ui.h"
#include "gfx.h"
#include "data.h"
#include "cfg.h"
static std::string wd = "sdmc:/JKSV/";
typedef struct
{
std::mutex bufferLock;
std::condition_variable cond;
std::vector<uint8_t> sharedBuffer;
std::string dst, dev;
bool bufferIsFull = false;
unsigned int filesize = 0, writeLimit = 0;
} fileCpyThreadArgs;
static void writeFile_t(void *a)
{
fileCpyThreadArgs *in = (fileCpyThreadArgs *)a;
size_t written = 0;
std::vector<uint8_t> localBuffer;
FILE *out = fopen(in->dst.c_str(), "wb");
while(written < in->filesize)
{
std::unique_lock<std::mutex> buffLock(in->bufferLock);
in->cond.wait(buffLock, [in]{ return in->bufferIsFull;});
localBuffer.clear();
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
in->sharedBuffer.clear();
in->bufferIsFull = false;
buffLock.unlock();
in->cond.notify_one();
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
}
fclose(out);
}
static void writeFileCommit_t(void *a)
{
fileCpyThreadArgs *in = (fileCpyThreadArgs *)a;
size_t written = 0, journalCount = 0;
std::vector<uint8_t> localBuffer;
FILE *out = fopen(in->dst.c_str(), "wb");
while(written < in->filesize)
{
std::unique_lock<std::mutex> buffLock(in->bufferLock);
in->cond.wait(buffLock, [in]{ return in->bufferIsFull; });
localBuffer.clear();
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
in->sharedBuffer.clear();
in->bufferIsFull = false;
buffLock.unlock();
in->cond.notify_one();
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
journalCount += written;
if(journalCount >= in->writeLimit)
{
journalCount = 0;
fclose(out);
fs::commitToDevice(in->dev.c_str());
out = fopen(in->dst.c_str(), "ab");
}
}
fclose(out);
}
fs::copyArgs *fs::copyArgsCreate(const std::string& src, const std::string& dst, const std::string& dev, zipFile z, unzFile unz, bool _cleanup, bool _trimZipPath, uint8_t _trimPlaces)
{
copyArgs *ret = new copyArgs;
ret->src = src;
ret->dst = dst;
ret->dev = dev;
ret->z = z;
ret->unz = unz;
ret->cleanup = _cleanup;
ret->prog = new ui::progBar;
ret->prog->setMax(0);
ret->prog->update(0);
ret->offset = 0;
ret->trimZipPath = _trimZipPath;
ret->trimZipPlaces = _trimPlaces;
return ret;
}
void fs::copyArgsDestroy(copyArgs *c)
{
delete c->prog;
delete c;
c = NULL;
}
fs::dataFile::dataFile(const std::string& _path)
{
f = fopen(_path.c_str(), "r");
if(f != NULL)
opened = true;
}
fs::dataFile::~dataFile()
{
fclose(f);
}
bool fs::dataFile::readNextLine(bool proc)
{
bool ret = false;
char tmp[1024];
while(fgets(tmp, 1024, f))
{
if(tmp[0] != '#' && tmp[0] != '\n' && tmp[0] != '\r')
{
line = tmp;
ret = true;
break;
}
}
util::stripChar('\n', line);
util::stripChar('\r', line);
if(proc)
procLine();
return ret;
}
void fs::dataFile::procLine()
{
size_t pPos = line.find_first_of("(=,");
if(pPos != line.npos)
{
lPos = pPos;
name.assign(line.begin(), line.begin() + lPos);
}
else
name = line;
util::stripChar(' ', name);
++lPos;
}
std::string fs::dataFile::getNextValueStr()
{
std::string ret = "";
//Skip all spaces until we hit actual text
size_t pos1 = line.find_first_not_of(", ", lPos);
//If reading from quotes
if(line[pos1] == '"')
lPos = line.find_first_of('"', ++pos1);
else
lPos = line.find_first_of(",;\n", pos1);//Set lPos to end of string we want. This should just set lPos to the end of the line if it fails, which is ok
ret = line.substr(pos1, lPos++ - pos1);
util::replaceStr(ret, "\\n", "\n");
return ret;
}
int fs::dataFile::getNextValueInt()
{
int ret = 0;
std::string no = getNextValueStr();
if(no[0] == '0' && tolower(no[1]) == 'x')
ret = strtoul(no.c_str(), NULL, 16);
else
ret = strtoul(no.c_str(), NULL, 10);
return ret;
}
void fs::copyFile(const std::string& src, const std::string& dst, threadInfo *t)
{
fs::copyArgs *c = NULL;
size_t filesize = fs::fsize(src);
if(t)
{
c = (fs::copyArgs *)t->argPtr;
c->offset = 0;
c->prog->setMax(filesize);
c->prog->update(0);
}
FILE *fsrc = fopen(src.c_str(), "rb");
if(!fsrc)
{
fclose(fsrc);
return;
}
fileCpyThreadArgs thrdArgs;
thrdArgs.dst = dst;
thrdArgs.filesize = filesize;
uint8_t *buff = new uint8_t[BUFF_SIZE];
std::vector<uint8_t> transferBuffer;
Thread writeThread;
threadCreate(&writeThread, writeFile_t, &thrdArgs, NULL, 0x40000, 0x2E, 2);
threadStart(&writeThread);
size_t readIn = 0;
uint64_t readCount = 0;
while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0)
{
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
readCount += readIn;
if(c)
c->offset = readCount;
if(transferBuffer.size() >= TRANSFER_BUFFER_LIMIT || readCount == filesize)
{
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; });
thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
transferBuffer.clear();
thrdArgs.bufferIsFull = true;
buffLock.unlock();
thrdArgs.cond.notify_one();
}
}
threadWaitForExit(&writeThread);
threadClose(&writeThread);
fclose(fsrc);
delete[] buff;
}
static void copyFileThreaded_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
t->status->setStatus(ui::getUICString("threadStatusCopyingFile", 0), in->src.c_str());
fs::copyFile(in->src, in->dst, t);
if(in->cleanup)
fs::copyArgsDestroy(in);
t->finished = true;
}
void fs::copyFileThreaded(const std::string& src, const std::string& dst)
{
fs::copyArgs *send = fs::copyArgsCreate(src, dst, "", NULL, NULL, true, false, 0);
ui::newThread(copyFileThreaded_t, send, fs::fileDrawFunc);
}
void fs::copyFileCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t)
{
fs::copyArgs *c = NULL;
size_t filesize = fs::fsize(src);
if(t)
{
c = (fs::copyArgs *)t->argPtr;
c->offset = 0;
c->prog->setMax(filesize);
c->prog->update(0);
}
FILE *fsrc = fopen(src.c_str(), "rb");
if(!fsrc)
{
fclose(fsrc);
return;
}
fileCpyThreadArgs thrdArgs;
thrdArgs.dst = dst;
thrdArgs.dev = dev;
thrdArgs.filesize = filesize;
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
uint64_t journalSpace = fs::getJournalSize(utinfo);
thrdArgs.writeLimit = (journalSpace - 0x100000) < TRANSFER_BUFFER_LIMIT ? journalSpace - 0x100000 : TRANSFER_BUFFER_LIMIT;
Thread writeThread;
threadCreate(&writeThread, writeFileCommit_t, &thrdArgs, NULL, 0x040000, 0x2E, 2);
uint8_t *buff = new uint8_t[BUFF_SIZE];
size_t readIn = 0;
uint64_t readCount = 0;
std::vector<uint8_t> transferBuffer;
threadStart(&writeThread);
while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0)
{
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
readCount += readIn;
if(c)
c->offset = readCount;
if(transferBuffer.size() >= thrdArgs.writeLimit || readCount == filesize)
{
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; });
thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
transferBuffer.clear();
thrdArgs.bufferIsFull = true;
buffLock.unlock();
thrdArgs.cond.notify_one();
}
}
threadWaitForExit(&writeThread);
threadClose(&writeThread);
fclose(fsrc);
fs::commitToDevice(dev);
delete[] buff;
}
static void copyFileCommit_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
t->status->setStatus(ui::getUICString("threadStatusCopyingFile", 0), in->src.c_str());
in->prog->setMax(fs::fsize(in->src));
in->prog->update(0);
fs::copyFileCommit(in->src, in->dst, in->dev, t);
if(in->cleanup)
fs::copyArgsDestroy(in);
t->finished = true;
}
void fs::copyFileCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev)
{
fs::copyArgs *send = fs::copyArgsCreate(src, dst, dev, NULL, NULL, true, false, 0);
ui::newThread(copyFileCommit_t, send, fs::fileDrawFunc);
}
void fs::fileDrawFunc(void *a)
{
threadInfo *t = (threadInfo *)a;
if(!t->finished && t->argPtr)
{
copyArgs *c = (copyArgs *)t->argPtr;
std::string tmp;
t->status->getStatus(tmp);
c->argLock();
c->prog->update(c->offset);
c->prog->draw(tmp);
c->argUnlock();
}
}
void fs::delfile(const std::string& path)
{
if(cfg::config["directFsCmd"])
fsremove(path.c_str());
else
remove(path.c_str());
}
void fs::getShowFileProps(const std::string& _path)
{
size_t size = fs::fsize(_path);
ui::showMessage(ui::getUICString("fileModeFileProperties", 0), _path.c_str(), util::getSizeString(size).c_str());
}
bool fs::fileExists(const std::string& path)
{
bool ret = false;
FILE *test = fopen(path.c_str(), "rb");
if(test != NULL)
ret = true;
fclose(test);
return ret;
}
size_t fs::fsize(const std::string& _f)
{
size_t ret = 0;
FILE *get = fopen(_f.c_str(), "rb");
if(get != NULL)
{
fseek(get, 0, SEEK_END);
ret = ftell(get);
}
fclose(get);
return ret;
}

150
src/fs/fsfile.c Normal file
View File

@ -0,0 +1,150 @@
#include <switch.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include "fs/fsfile.h"
char *getDeviceFromPath(char *dev, size_t _max, const char *path)
{
memset(dev, 0, _max);
char *c = strchr(path, ':');
if(c - path > _max)
return NULL;
//probably not good? idk
memcpy(dev, path, c - path);
return dev;
}
char *getFilePath(char *pathOut, size_t _max, const char *path)
{
memset(pathOut, 0, _max);
char *c = strchr(path, '/');
size_t pLength = strlen(c);
if(pLength > _max)
return NULL;
memcpy(pathOut, c, pLength);
return pathOut;
}
bool fsMkDir(const char *_p)
{
char devStr[16];
char path[FS_MAX_PATH];
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(path, FS_MAX_PATH, _p))
return false;
Result res = fsFsCreateDirectory(fsdevGetDeviceFileSystem(devStr), path);
return res == 0;
}
int fsremove(const char *_p)
{
char devStr[16];
char path[FS_MAX_PATH];
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(path, FS_MAX_PATH, _p))
return -1;
Result res = fsFsDeleteFile(fsdevGetDeviceFileSystem(devStr), path);
return res;
}
Result fsDelDirRec(const char *_p)
{
char devStr[16];
char path[FS_MAX_PATH];
if(!getDeviceFromPath(devStr, 16, _p) || ! getFilePath(path, FS_MAX_PATH, _p))
return 1;
return fsFsDeleteDirectoryRecursively(fsdevGetDeviceFileSystem(devStr), path);
}
bool fsfcreate(const char *_p, int64_t crSize)
{
char devStr[16];
char filePath[FS_MAX_PATH];
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(filePath, FS_MAX_PATH, _p))
return false;
FsFileSystem *s = fsdevGetDeviceFileSystem(devStr);
if(s == NULL)
return false;
Result res = fsFsCreateFile(s, filePath, crSize, 0);
if(R_SUCCEEDED(res))
res = fsdevCommitDevice(devStr);
return R_SUCCEEDED(res) ? true : false;
}
FSFILE *fsfopen(const char *_p, uint32_t mode)
{
char devStr[16];
char filePath[FS_MAX_PATH];
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(filePath, FS_MAX_PATH, _p))
return NULL;
FsFileSystem *s = fsdevGetDeviceFileSystem(devStr);
if(s == NULL)
return NULL;
if(mode == FsOpenMode_Write)
{
fsFsDeleteFile(s, filePath);
fsFsCreateFile(s, filePath, 0, 0);
}
FSFILE *ret = malloc(sizeof(FSFILE));
ret->error = fsFsOpenFile(s, filePath, mode, &ret->_f);
if(R_FAILED(ret->error))
{
free(ret);
return NULL;
}
fsFileGetSize(&ret->_f, &ret->fsize);
ret->offset = (mode & FsOpenMode_Append) ? ret->fsize : 0;
return ret;
}
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode)
{
if(mode & FsOpenMode_Write)
{
fsFsDeleteFile(_s, _p);
fsFsCreateFile(_s, _p, 0, 0);
}
else if(mode & FsOpenMode_Append)
fsFsCreateFile(_s, _p, 0, 0);
FSFILE *ret = malloc(sizeof(FSFILE));
ret->error = fsFsOpenFile(_s, _p, mode, &ret->_f);
if(R_FAILED(ret->error))
{
free(ret);
return NULL;
}
fsFileGetSize(&ret->_f, &ret->fsize);
ret->offset = (mode & FsOpenMode_Append) ? ret->fsize : 0;
return ret;
}
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f)
{
size_t fullSize = sz * count;
if(_f->offset + fullSize > _f->fsize)
{
s64 newSize = (_f->fsize + fullSize) - (_f->fsize - _f->offset);
fsFileSetSize(&_f->_f, newSize);
_f->fsize = newSize;
}
_f->error = fsFileWrite(&_f->_f, _f->offset, buf, fullSize, FsWriteOption_Flush);
_f->offset += fullSize;
return fullSize;
}

134
src/fs/remote.cpp Normal file
View File

@ -0,0 +1,134 @@
#include "fs.h"
#include "rfs.h"
#include "gd.h"
#include "webdav.h"
#include "cfg.h"
#include "ui.h"
rfs::IRemoteFS *fs::rfs = NULL;
std::string fs::rfsRootID;
void fs::remoteInit()
{
// Google Drive has priority
driveInit();
webDavInit();
}
void fs::remoteExit()
{
if(rfs) {
delete rfs;
rfs = NULL;
}
}
void fs::driveInit()
{
// Already initialized?
if (rfs)
return;
if(cfg::driveClientID.empty() || cfg::driveClientSecret.empty())
return;
bool refreshed = false, exchanged = false;
drive::gd *gDrive = new drive::gd;
gDrive->setClientID(cfg::driveClientID);
gDrive->setClientSecret(cfg::driveClientSecret);
if(!cfg::driveRefreshToken.empty())
{
gDrive->setRefreshToken(cfg::driveRefreshToken);
refreshed = gDrive->refreshToken();
}
if(!refreshed)
{
std::string authCode = driveSignInGetAuthCode();
exchanged = gDrive->exhangeAuthCode(authCode);
}
if(gDrive->hasToken())
{
if(exchanged)
{
cfg::driveRefreshToken = gDrive->getRefreshToken();
cfg::saveConfig();
}
gDrive->driveListInit("");
if(!gDrive->dirExists(JKSV_DRIVE_FOLDER))
gDrive->createDir(JKSV_DRIVE_FOLDER, "");
rfsRootID = gDrive->getDirID(JKSV_DRIVE_FOLDER);
rfs = gDrive;
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveStarted", 0));
}
else
{
delete gDrive;
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveFailed", 0));
}
}
std::string fs::driveSignInGetAuthCode()
{
std::string url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=" + cfg::driveClientID + "&redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto&response_type=code&scope=https://www.googleapis.com/auth/drive";
std::string replyURL;
WebCommonConfig webCfg;
WebCommonReply webReply;
webPageCreate(&webCfg, url.c_str());
webConfigSetCallbackUrl(&webCfg, "https://accounts.google.com/o/oauth2/approval/");
webConfigShow(&webCfg, &webReply);
size_t rLength = 0;
char replyURLCstr[0x1000];
webReplyGetLastUrl(&webReply, replyURLCstr, 0x1000, &rLength);
//Prevent crash if empty.
if(strlen(replyURLCstr) == 0)
return "";
replyURL.assign(replyURLCstr);
int unescLength = 0;
size_t codeBegin = replyURL.find("approvalCode") + 13, codeEnd = replyURL.find_last_of('#');
size_t codeLength = codeEnd - codeBegin;
replyURL = replyURL.substr(codeBegin, codeLength);
char *urlUnesc = curl_easy_unescape(NULL, replyURL.c_str(), replyURL.length(), &unescLength);
replyURL = urlUnesc;
curl_free(urlUnesc);
//Finally
return replyURL;
}
void fs::webDavInit() {
// Already initialized?
if (rfs)
return;
if (cfg::webdavOrigin.empty())
return;
rfs::WebDav *webdav = new rfs::WebDav(cfg::webdavOrigin,
cfg::webdavUser,
cfg::webdavPassword);
std::string baseId = "/" + cfg::webdavBasePath + (cfg::webdavBasePath.empty() ? "" : "/");
rfsRootID = webdav->getDirID(JKSV_DRIVE_FOLDER, baseId);
// check access
if (!webdav->dirExists(JKSV_DRIVE_FOLDER, baseId)) // this could return false on auth/config related errors
{
if (!webdav->createDir(JKSV_DRIVE_FOLDER, baseId))
{
delete webdav;
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popWebdavFailed", 0));
return;
}
}
rfs = webdav;
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popWebdavStarted", 0));
}

256
src/fs/zip.cpp Normal file
View File

@ -0,0 +1,256 @@
#include <switch.h>
#include <time.h>
#include <mutex>
#include <vector>
#include <condition_variable>
#include "fs.h"
#include "util.h"
#include "cfg.h"
typedef struct
{
std::mutex buffLock;
std::condition_variable cond;
std::vector<uint8_t> sharedBuffer;
std::string dst, dev;
bool bufferIsFull = false;
unzFile unz;
unsigned int fileSize, writeLimit = 0;
} unzThrdArgs;
static void writeFileFromZip_t(void *a)
{
unzThrdArgs *in = (unzThrdArgs *)a;
std::vector<uint8_t> localBuffer;
unsigned int written = 0, journalCount = 0;
FILE *out = fopen(in->dst.c_str(), "wb");
while(written < in->fileSize)
{
std::unique_lock<std::mutex> buffLock(in->buffLock);
in->cond.wait(buffLock, [in]{ return in->bufferIsFull; });
localBuffer.clear();
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
in->sharedBuffer.clear();
in->bufferIsFull = false;
buffLock.unlock();
in->cond.notify_one();
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
journalCount += written;
if(journalCount >= in->writeLimit)
{
journalCount = 0;
fclose(out);
fs::commitToDevice(in->dev);
out = fopen(in->dst.c_str(), "ab");
}
}
fclose(out);
}
void fs::copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int trimPlaces, threadInfo *t)
{
fs::copyArgs *c = NULL;
if(t)
{
t->status->setStatus(ui::getUICString("threadStatusOpeningFolder", 0), src.c_str());
c = (fs::copyArgs *)t->argPtr;
}
fs::dirList *list = new fs::dirList(src);
for(unsigned i = 0; i < list->getCount(); i++)
{
std::string itm = list->getItem(i);
if(fs::pathIsFiltered(src + itm))
continue;
if(list->isDir(i))
{
std::string newSrc = src + itm + "/";
fs::copyDirToZip(newSrc, dst, trimPath, trimPlaces, t);
}
else
{
time_t raw;
time(&raw);
tm *locTime = localtime(&raw);
zip_fileinfo inf = { locTime->tm_sec, locTime->tm_min, locTime->tm_hour,
locTime->tm_mday, locTime->tm_mon, (1900 + locTime->tm_year), 0, 0, 0 };
std::string filename = src + itm;
size_t zipNameStart = 0;
if(trimPath)
util::trimPath(filename, trimPlaces);
else
zipNameStart = filename.find_first_of('/') + 1;
if(t)
t->status->setStatus(ui::getUICString("threadStatusAddingFileToZip", 0), itm.c_str());
int zipOpenFile = zipOpenNewFileInZip64(dst, filename.substr(zipNameStart, filename.npos).c_str(), &inf, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0);
if(zipOpenFile == ZIP_OK)
{
std::string fullSrc = src + itm;
if(c)
{
c->offset = 0;
c->prog->setMax(fs::fsize(fullSrc));
c->prog->update(0);
}
FILE *fsrc = fopen(fullSrc.c_str(), "rb");
size_t readIn = 0;
uint8_t *buff = new uint8_t[ZIP_BUFF_SIZE];
while((readIn = fread(buff, 1, ZIP_BUFF_SIZE, fsrc)) > 0)
{
zipWriteInFileInZip(dst, buff, readIn);
if(c)
c->offset += readIn;
}
delete[] buff;
fclose(fsrc);
}
}
}
}
void copyDirToZip_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
if(cfg::config["ovrClk"])
{
util::sysBoost();
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popCPUBoostEnabled", 0));
}
fs::copyDirToZip(c->src, c->z, c->trimZipPath, c->trimZipPlaces, t);
if(cfg::config["ovrClk"])
util::sysNormal();
if(c->cleanup)
{
zipClose(c->z, NULL);
delete c;
}
t->finished = true;
}
void fs::copyDirToZipThreaded(const std::string& src, zipFile dst, bool trimPath, int trimPlaces)
{
fs::copyArgs *send = fs::copyArgsCreate(src, "", "", dst, NULL, true, false, 0);
ui::newThread(copyDirToZip_t, send, fs::fileDrawFunc);
}
void fs::copyZipToDir(unzFile src, const std::string& dst, const std::string& dev, threadInfo *t)
{
fs::copyArgs *c = NULL;
if(t)
c = (fs::copyArgs *)t->argPtr;
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
uint64_t journalSize = getJournalSize(utinfo);
char filename[FS_MAX_PATH];
uint8_t *buff = new uint8_t[BUFF_SIZE];
int readIn = 0;
unz_file_info64 info;
do
{
unzGetCurrentFileInfo64(src, &info, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
if(unzOpenCurrentFile(src) == UNZ_OK)
{
if(t)
t->status->setStatus(ui::getUICString("threadStatusDecompressingFile", 0), filename);
if(c)
{
c->prog->setMax(info.uncompressed_size);
c->prog->update(0);
c->offset = 0;
}
std::string fullDst = dst + filename;
fs::mkDirRec(fullDst.substr(0, fullDst.find_last_of('/') + 1));
unzThrdArgs unzThrd;
unzThrd.dst = fullDst;
unzThrd.fileSize = info.uncompressed_size;
unzThrd.dev = dev;
unzThrd.writeLimit = (journalSize - 0x100000) < TRANSFER_BUFFER_LIMIT ? (journalSize - 0x100000) : TRANSFER_BUFFER_LIMIT;
Thread writeThread;
threadCreate(&writeThread, writeFileFromZip_t, &unzThrd, NULL, 0x8000, 0x2B, 2);
threadStart(&writeThread);
std::vector<uint8_t> transferBuffer;
uint64_t readCount = 0;
while((readIn = unzReadCurrentFile(src, buff, BUFF_SIZE)) > 0)
{
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
readCount += readIn;
if(c)
c->offset += readIn;
if(transferBuffer.size() >= unzThrd.writeLimit || readCount == info.uncompressed_size)
{
std::unique_lock<std::mutex> buffLock(unzThrd.buffLock);
unzThrd.cond.wait(buffLock, [&unzThrd]{ return unzThrd.bufferIsFull == false; });
unzThrd.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
transferBuffer.clear();
unzThrd.bufferIsFull = true;
unzThrd.cond.notify_one();
}
}
threadWaitForExit(&writeThread);
threadClose(&writeThread);
fs::commitToDevice(dev);
}
}
while(unzGoToNextFile(src) != UNZ_END_OF_LIST_OF_FILE);
delete[] buff;
}
static void copyZipToDir_t(void *a)
{
threadInfo *t = (threadInfo *)a;
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
fs::copyZipToDir(c->unz, c->dst, c->dev, t);
if(c->cleanup)
{
unzClose(c->unz);
delete c;
}
t->finished = true;
}
void fs::copyZipToDirThreaded(unzFile src, const std::string& dst, const std::string& dev)
{
fs::copyArgs *send = fs::copyArgsCreate("", dst, dev, NULL, src, true, false, 0);
ui::newThread(copyZipToDir_t, send, fs::fileDrawFunc);
}
uint64_t fs::getZipTotalSize(unzFile unz)
{
uint64_t ret = 0;
if(unzGoToFirstFile(unz) == UNZ_OK)
{
unz_file_info64 finfo;
char filename[FS_MAX_PATH];
do
{
unzGetCurrentFileInfo64(unz, &finfo, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
ret += finfo.uncompressed_size;
} while(unzGoToNextFile(unz) != UNZ_END_OF_LIST_OF_FILE);
unzGoToFirstFile(unz);
}
return ret;
}
bool fs::zipNotEmpty(unzFile unz)
{
return unzGoToFirstFile(unz) == UNZ_OK;
}

646
src/gd.cpp Normal file
View File

@ -0,0 +1,646 @@
#include <stdio.h>
#include <curl/curl.h>
#include <json-c/json.h>
#include <string>
#include <vector>
#include <mutex>
#include <condition_variable>
#include "gd.h"
#include "fs.h"
#include "curlfuncs.h"
#include "util.h"
/*
Google Drive code for JKSV.
Still major WIP
*/
#define DRIVE_DEFAULT_PARAMS_AND_QUERY "?fields=files(name,id,mimeType,size,parents)&pageSize=1000&q=trashed=false\%20and\%20\%27me\%27\%20in\%20owners"
#define tokenURL "https://oauth2.googleapis.com/token"
#define tokenCheckURL "https://oauth2.googleapis.com/tokeninfo"
#define driveURL "https://www.googleapis.com/drive/v3/files"
#define driveUploadURL "https://www.googleapis.com/upload/drive/v3/files"
static inline void writeDriveError(const std::string& _function, const std::string& _message)
{
fs::logWrite("Drive/%s: %s\n", _function.c_str(), _message.c_str());
}
static inline void writeCurlError(const std::string& _function, int _cerror)
{
fs::logWrite("Drive/%s: CURL returned error %i\n", _function.c_str(), _cerror);
}
bool drive::gd::exhangeAuthCode(const std::string& _authCode)
{
// Header
curl_slist *postHeader = NULL;
postHeader = curl_slist_append(postHeader, HEADER_CONTENT_TYPE_APP_JSON);
// Post json
json_object *post = json_object_new_object();
json_object *clientIDString = json_object_new_string(clientID.c_str());
json_object *secretIDString = json_object_new_string(secretID.c_str());
json_object *authCodeString = json_object_new_string(_authCode.c_str());
json_object *redirectUriString = json_object_new_string("urn:ietf:wg:oauth:2.0:oob:auto");
json_object *grantTypeString = json_object_new_string("authorization_code");
json_object_object_add(post, "client_id", clientIDString);
json_object_object_add(post, "client_secret", secretIDString);
json_object_object_add(post, "code", authCodeString);
json_object_object_add(post, "redirect_uri", redirectUriString);
json_object_object_add(post, "grant_type", grantTypeString);
// Curl Request
std::string *jsonResp = new std::string;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeader);
curl_easy_setopt(curl, CURLOPT_URL, tokenURL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_get_string(post));
int error = curl_easy_perform(curl);
json_object *respParse = json_tokener_parse(jsonResp->c_str());
if (error == CURLE_OK)
{
json_object *accessToken = json_object_object_get(respParse, "access_token");
json_object *refreshToken = json_object_object_get(respParse, "refresh_token");
if(accessToken && refreshToken)
{
token = json_object_get_string(accessToken);
rToken = json_object_get_string(refreshToken);
}
else
writeDriveError("exchangeAuthCode", jsonResp->c_str());
}
else
writeCurlError("exchangeAuthCode", error);
delete jsonResp;
json_object_put(post);
json_object_put(respParse);
curl_slist_free_all(postHeader);
curl_easy_cleanup(curl);
return true;
}
bool drive::gd::refreshToken()
{
bool ret = false;
// Header
curl_slist *header = NULL;
header = curl_slist_append(header, HEADER_CONTENT_TYPE_APP_JSON);
// Post Json
json_object *post = json_object_new_object();
json_object *clientIDString = json_object_new_string(clientID.c_str());
json_object *secretIDString = json_object_new_string(secretID.c_str());
json_object *refreshTokenString = json_object_new_string(rToken.c_str());
json_object *grantTypeString = json_object_new_string("refresh_token");
json_object_object_add(post, "client_id", clientIDString);
json_object_object_add(post, "client_secret", secretIDString);
json_object_object_add(post, "refresh_token", refreshTokenString);
json_object_object_add(post, "grant_type", grantTypeString);
// Curl
std::string *jsonResp = new std::string;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curl, CURLOPT_URL, tokenURL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_get_string(post));
int error = curl_easy_perform(curl);
json_object *parse = json_tokener_parse(jsonResp->c_str());
if (error == CURLE_OK)
{
json_object *accessToken, *error;
json_object_object_get_ex(parse, "access_token", &accessToken);
json_object_object_get_ex(parse, "error", &error);
if(accessToken)
{
token = json_object_get_string(accessToken);
ret = true;
}
else if(error)
writeDriveError("refreshToken", jsonResp->c_str());
}
delete jsonResp;
json_object_put(post);
json_object_put(parse);
curl_slist_free_all(header);
curl_easy_cleanup(curl);
return ret;
}
bool drive::gd::tokenIsValid()
{
bool ret = false;
std::string url = tokenCheckURL;
url.append("?access_token=" + token);
CURL *curl = curl_easy_init();
std::string *jsonResp = new std::string;
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp);
int error = curl_easy_perform(curl);
json_object *parse = json_tokener_parse(jsonResp->c_str());
if (error == CURLE_OK)
{
json_object *checkError;
json_object_object_get_ex(parse, "error", &checkError);
if(!checkError)
ret = true;
}
delete jsonResp;
json_object_put(parse);
curl_easy_cleanup(curl);
return ret;
}
static int requestList(const std::string& _url, const std::string& _token, std::string *_respOut)
{
int ret = 0;
// Headers needed
curl_slist *postHeaders = NULL;
postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + _token).c_str());
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders);
curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, _respOut);
ret = curl_easy_perform(curl);
curl_slist_free_all(postHeaders);
curl_easy_cleanup(curl);
return ret;
}
static void processList(const std::string& _json, std::vector<rfs::RfsItem>& _drvl, bool _clear)
{
if(_clear)
_drvl.clear();
json_object *parse = json_tokener_parse(_json.c_str()), *fileArray;
json_object_object_get_ex(parse, "files", &fileArray);
if(fileArray)
{
size_t arrayLength = json_object_array_length(fileArray);
_drvl.reserve(_drvl.size() + arrayLength);
for(unsigned i = 0; i < arrayLength; i++)
{
json_object *idString, *nameString, *mimeTypeString, *size, *parentArray;
json_object *curFile = json_object_array_get_idx(fileArray, i);
json_object_object_get_ex(curFile, "id", &idString);
json_object_object_get_ex(curFile, "name", &nameString);
json_object_object_get_ex(curFile, "mimeType", &mimeTypeString);
json_object_object_get_ex(curFile, "size", &size);
json_object_object_get_ex(curFile, "parents", &parentArray);
rfs::RfsItem newDirItem;
newDirItem.name = json_object_get_string(nameString);
newDirItem.id = json_object_get_string(idString);
newDirItem.size = json_object_get_int(size);
if(strcmp(json_object_get_string(mimeTypeString), MIMETYPE_FOLDER) == 0)
newDirItem.isDir = true;
if (parentArray)
{
size_t parentCount = json_object_array_length(parentArray);
//There can only be 1 parent, but it's held in an array...
for (unsigned j = 0; j < parentCount; j++)
{
json_object *parent = json_object_array_get_idx(parentArray, j);
newDirItem.parent = json_object_get_string(parent);
}
}
_drvl.push_back(newDirItem);
}
}
json_object_put(parse);
}
void drive::gd::driveListInit(const std::string& _q)
{
if(!tokenIsValid())
refreshToken();
// Request url with specific fields needed.
std::string url = std::string(driveURL) + std::string(DRIVE_DEFAULT_PARAMS_AND_QUERY);
if(!_q.empty())
{
char *qEsc = curl_easy_escape(NULL, _q.c_str(), _q.length());
url.append(std::string("\%20and\%20") + std::string(qEsc));
curl_free(qEsc);
}
std::string jsonResp;
int error = requestList(url, token, &jsonResp);
if(error == CURLE_OK)
processList(jsonResp, driveList, true);
else
writeCurlError("driveListInit", error);
}
void drive::gd::driveListAppend(const std::string& _q)
{
if(!tokenIsValid())
refreshToken();
std::string url = std::string(driveURL) + std::string(DRIVE_DEFAULT_PARAMS_AND_QUERY);
if(!_q.empty())
{
char *qEsc = curl_easy_escape(NULL, _q.c_str(), _q.length());
url.append(std::string("\%20and\%20") + std::string(qEsc));
curl_free(qEsc);
}
std::string jsonResp;
int error = requestList(url, token, &jsonResp);
if(error == CURLE_OK)
processList(jsonResp, driveList, false);
else
writeCurlError("driveListAppend", error);
}
std::vector<rfs::RfsItem> drive::gd::getListWithParent(const std::string& _parent) {
std::vector<rfs::RfsItem> filtered;
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].parent == _parent)
filtered.push_back(driveList[i]);
}
return filtered;
}
void drive::gd::debugWriteList()
{
for(auto& di : driveList)
{
fs::logWrite("%s\n\t%s\n", di.name.c_str(), di.id.c_str());
if(!di.parent.empty())
fs::logWrite("\t%s\n", di.parent.c_str());
}
}
bool drive::gd::createDir(const std::string& _dirName, const std::string& _parent)
{
if(!tokenIsValid())
refreshToken();
bool ret = true;
// Headers to use
curl_slist *postHeaders = NULL;
postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + token).c_str());
postHeaders = curl_slist_append(postHeaders, HEADER_CONTENT_TYPE_APP_JSON);
// JSON To Post
json_object *post = json_object_new_object();
json_object *nameString = json_object_new_string(_dirName.c_str());
json_object *mimeTypeString = json_object_new_string(MIMETYPE_FOLDER);
json_object_object_add(post, "name", nameString);
json_object_object_add(post, "mimeType", mimeTypeString);
if (!_parent.empty())
{
json_object *parentsArray = json_object_new_array();
json_object *parentString = json_object_new_string(_parent.c_str());
json_object_array_add(parentsArray, parentString);
json_object_object_add(post, "parents", parentsArray);
}
// Curl Request
std::string *jsonResp = new std::string;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders);
curl_easy_setopt(curl, CURLOPT_URL, driveURL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_get_string(post));
int error = curl_easy_perform(curl);
json_object *respParse = json_tokener_parse(jsonResp->c_str()), *checkError;
json_object_object_get_ex(respParse, "error", &checkError);
if (error == CURLE_OK && !checkError)
{
//Append it to list
json_object *id;
json_object_object_get_ex(respParse, "id", &id);
rfs::RfsItem newDir;
newDir.name = _dirName;
newDir.id = json_object_get_string(id);
newDir.isDir = true;
newDir.size = 0;
newDir.parent = _parent;
driveList.push_back(newDir);
}
else
ret = false;
delete jsonResp;
json_object_put(post);
json_object_put(respParse);
curl_slist_free_all(postHeaders);
curl_easy_cleanup(curl);
return ret;
}
bool drive::gd::dirExists(const std::string& _dirName)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].isDir && driveList[i].name == _dirName)
return true;
}
return false;
}
bool drive::gd::dirExists(const std::string& _dirName, const std::string& _parent)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].isDir && driveList[i].name == _dirName && driveList[i].parent == _parent)
return true;
}
return false;
}
bool drive::gd::fileExists(const std::string& _filename, const std::string& _parent)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(!driveList[i].isDir && driveList[i].name == _filename && driveList[i].parent == _parent)
return true;
}
return false;
}
void drive::gd::uploadFile(const std::string& _filename, const std::string& _parent, curlFuncs::curlUpArgs *_upload)
{
if(!tokenIsValid())
refreshToken();
std::string url = driveUploadURL;
url.append("?uploadType=resumable");
// Headers
curl_slist *postHeaders = NULL;
postHeaders = curl_slist_append(postHeaders, std::string(HEADER_AUTHORIZATION + token).c_str());
postHeaders = curl_slist_append(postHeaders, HEADER_CONTENT_TYPE_APP_JSON);
// Post JSON
json_object *post = json_object_new_object();
json_object *nameString = json_object_new_string(_filename.c_str());
json_object_object_add(post, "name", nameString);
if (!_parent.empty())
{
json_object *parentArray = json_object_new_array();
json_object *parentString = json_object_new_string(_parent.c_str());
json_object_array_add(parentArray, parentString);
json_object_object_add(post, "parents", parentArray);
}
// Curl upload request
std::string *jsonResp = new std::string;
std::vector<std::string> *headers = new std::vector<std::string>;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, postHeaders);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlFuncs::writeHeaders);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_object_get_string(post));
int error = curl_easy_perform(curl);
std::string location = curlFuncs::getHeader("Location", headers);
if (error == CURLE_OK && location != HEADER_ERROR)
{
CURL *curlUp = curl_easy_init();
curl_easy_setopt(curlUp, CURLOPT_PUT, 1);
curl_easy_setopt(curlUp, CURLOPT_URL, location.c_str());
curl_easy_setopt(curlUp, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curlUp, CURLOPT_WRITEDATA, jsonResp);
curl_easy_setopt(curlUp, CURLOPT_READFUNCTION, curlFuncs::readDataFile);
curl_easy_setopt(curlUp, CURLOPT_READDATA, _upload);
curl_easy_setopt(curlUp, CURLOPT_UPLOAD_BUFFERSIZE, UPLOAD_BUFFER_SIZE);
curl_easy_setopt(curlUp, CURLOPT_UPLOAD, 1);
curl_easy_perform(curlUp);
curl_easy_cleanup(curlUp);
json_object *parse = json_tokener_parse(jsonResp->c_str()), *id, *name, *mimeType;
json_object_object_get_ex(parse, "id", &id);
json_object_object_get_ex(parse, "name", &name);
json_object_object_get_ex(parse, "mimeType", &mimeType);
if(name && id && mimeType)
{
rfs::RfsItem uploadData;
uploadData.id = json_object_get_string(id);
uploadData.name = json_object_get_string(name);
uploadData.isDir = false;
uploadData.size = *_upload->o;//should be safe to use
uploadData.parent = _parent;
driveList.push_back(uploadData);
}
json_object_put(parse);
}
else
writeCurlError("uploadFile", error);
delete jsonResp;
delete headers;
json_object_put(post);
curl_slist_free_all(postHeaders);
}
void drive::gd::updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload)
{
if(!tokenIsValid())
refreshToken();
//URL
std::string url = driveUploadURL;
url.append("/" + _fileID);
url.append("?uploadType=resumable");
//Header
curl_slist *patchHeader = NULL;
patchHeader = curl_slist_append(patchHeader, std::string(HEADER_AUTHORIZATION + token).c_str());
//Curl
std::string *jsonResp = new std::string;
std::vector<std::string> *headers = new std::vector<std::string>;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, patchHeader);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlFuncs::writeHeaders);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, jsonResp);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, headers);
int error = curl_easy_perform(curl);
std::string location = curlFuncs::getHeader("Location", headers);
if(error == CURLE_OK && location != HEADER_ERROR)
{
CURL *curlPatch = curl_easy_init();
curl_easy_setopt(curlPatch, CURLOPT_PUT, 1);
curl_easy_setopt(curlPatch, CURLOPT_URL, location.c_str());
curl_easy_setopt(curlPatch, CURLOPT_READFUNCTION, curlFuncs::readDataFile);
curl_easy_setopt(curlPatch, CURLOPT_READDATA, _upload);
curl_easy_setopt(curlPatch, CURLOPT_UPLOAD_BUFFERSIZE, UPLOAD_BUFFER_SIZE);
curl_easy_setopt(curlPatch, CURLOPT_UPLOAD, 1);
curl_easy_perform(curlPatch);
curl_easy_cleanup(curlPatch);
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].id == _fileID)
{
driveList[i].size = *_upload->o;
break;
}
}
}
delete jsonResp;
delete headers;
curl_slist_free_all(patchHeader);
curl_easy_cleanup(curl);
}
void drive::gd::downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download)
{
if(!tokenIsValid())
refreshToken();
//URL
std::string url = driveURL;
url.append("/" + _fileID);
url.append("?alt=media");
//Headers
curl_slist *getHeaders = NULL;
getHeaders = curl_slist_append(getHeaders, std::string(HEADER_AUTHORIZATION + token).c_str());
//Downloading is threaded because it's too slow otherwise
rfs::dlWriteThreadStruct dlWrite;
dlWrite.cfa = _download;
Thread writeThread;
threadCreate(&writeThread, rfs::writeThread_t, &dlWrite, NULL, 0x8000, 0x2B, 2);
//Curl
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, getHeaders);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, rfs::writeDataBufferThreaded);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dlWrite);
threadStart(&writeThread);
curl_easy_perform(curl);
threadWaitForExit(&writeThread);
threadClose(&writeThread);
curl_slist_free_all(getHeaders);
curl_easy_cleanup(curl);
}
void drive::gd::deleteFile(const std::string& _fileID)
{
if(!tokenIsValid())
refreshToken();
//URL
std::string url = driveURL;
url.append("/" + _fileID);
//Header
curl_slist *delHeaders = NULL;
delHeaders = curl_slist_append(delHeaders, std::string(HEADER_AUTHORIZATION + token).c_str());
//Curl
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, delHeaders);
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_perform(curl);
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].id == _fileID)
{
driveList.erase(driveList.begin() + i);
break;
}
}
curl_slist_free_all(delHeaders);
curl_easy_cleanup(curl);
}
std::string drive::gd::getFileID(const std::string& _name, const std::string& _parent)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(!driveList[i].isDir && driveList[i].name == _name && driveList[i].parent == _parent)
return driveList[i].id;
}
return "";
}
std::string drive::gd::getDirID(const std::string& _name)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].isDir && driveList[i].name == _name)
return driveList[i].id;
}
return "";
}
std::string drive::gd::getDirID(const std::string& _name, const std::string& _parent)
{
for(unsigned i = 0; i < driveList.size(); i++)
{
if(driveList[i].isDir && driveList[i].name == _name && driveList[i].parent == _parent)
return driveList[i].id;
}
return "";
}

Some files were not shown because too many files have changed in this diff Show More