mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-03-22 01:34:13 -05:00
Compare commits
40 Commits
02/23/2023
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c094030c52 | ||
|
|
291d2fd82f | ||
|
|
df03aac552 | ||
|
|
24aafcbf63 | ||
|
|
1e06516317 | ||
|
|
da121677fb | ||
|
|
361b862bf3 | ||
|
|
790e4bebdf | ||
|
|
9ac5f39476 | ||
|
|
44a466fef0 | ||
|
|
04ce90c7dd | ||
|
|
ddbf601819 | ||
|
|
9b8c852b5c | ||
|
|
ddcebdcff6 | ||
|
|
cf39346d69 | ||
|
|
7949582fd8 | ||
|
|
1bda18d98f | ||
|
|
3f39769b52 | ||
|
|
8c3c58cadc | ||
|
|
e1634556ca | ||
|
|
c2cf998d10 | ||
|
|
f9668895eb | ||
|
|
2cf5a22062 | ||
|
|
df89d8b116 | ||
|
|
24644bb743 | ||
|
|
1aca15ae6e | ||
|
|
6b8c3da184 | ||
|
|
ef633b1752 | ||
|
|
f342bf15af | ||
|
|
938c52b603 | ||
|
|
a893d3fa56 | ||
|
|
69901a93be | ||
|
|
aee8d777fc | ||
|
|
96cdb7f488 | ||
|
|
1ac23d2750 | ||
|
|
85bca2d66e | ||
|
|
6f8393483f | ||
|
|
14a2debccd | ||
|
|
83228a2911 | ||
|
|
07b210b0bd |
136
.clang-format
Normal file
136
.clang-format
Normal 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
9
.clang-tidy
Normal 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
11
.gitattributes
vendored
Normal 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
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,4 +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
50
CMakeLists.txt
Normal 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
42
JKSV_Settings_ENG.MD
Normal 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_.
|
||||
15
Makefile
15
Makefile
|
|
@ -32,13 +32,13 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||
#---------------------------------------------------------------------------------
|
||||
TARGET := JKSV
|
||||
BUILD := build
|
||||
SOURCES := src src/ui src/fs
|
||||
SOURCES := src src/ui src/fs src/gfx
|
||||
DATA := data
|
||||
INCLUDES := inc inc/ui inc/fs
|
||||
INCLUDES := inc inc/ui inc/fs inc/gfx
|
||||
EXEFS_SRC := exefs_src
|
||||
APP_TITLE := JKSV
|
||||
APP_AUTHOR := JK
|
||||
APP_VERSION := 02.12.2023
|
||||
APP_VERSION := 11.5.2024
|
||||
ROMFS := romfs
|
||||
ICON := icon.jpg
|
||||
|
||||
|
|
@ -47,17 +47,16 @@ ICON := icon.jpg
|
|||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
override CFLAGS += `sdl2-config --cflags` -g -Wall -O2 -ffunction-sections -ffast-math \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
override CFLAGS += $(INCLUDE) -D__SWITCH__ `freetype-config --cflags` `curl-config --cflags`
|
||||
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)
|
||||
|
||||
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 := `sdl2-config --libs` `freetype-config --libs` `curl-config --libs` -lSDL2_image -lwebp -lpng -ljpeg -lz -lminizip -ljson-c -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
|
||||
|
|
|
|||
33
README.MD
33
README.MD
|
|
@ -12,26 +12,27 @@ This started as a simple, straight port of my 3DS save manager I publicly releas
|
|||
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.
|
||||
* 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](https://github.com/J-D-K/JKSV/blob/master/GD_INSTRUCTIONS.MD) if it is configured.
|
||||
8. 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).
|
||||
9. 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.
|
||||
10. Extend save data containers to any size the user wants or automatically if the save cannot fit into the current one.
|
||||
11. Delete save data from the system.
|
||||
12. Reset save data as if the game was never launched.
|
||||
13. 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.
|
||||
14. Open and explore bis storage partitions via the Extras menu
|
||||
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.
|
||||
15. Misc Extras:
|
||||
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.
|
||||
* 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.
|
||||
|
||||
**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.**
|
||||
|
||||
|
|
@ -45,7 +46,7 @@ This started as a simple, straight port of my 3DS save manager I publicly releas
|
|||
* 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 permanant.
|
||||
* Delete All Save Data deletes all save data for the selected user. This is permanent.
|
||||
* Settings and Extras below.
|
||||
|
||||
2. Title/Game Select
|
||||
|
|
@ -97,7 +98,7 @@ This started as a simple, straight port of my 3DS save manager I publicly releas
|
|||
|
||||
## Building:
|
||||
1. Requires [devkitPro](https://devkitpro.org/) and [libnx](https://github.com/switchbrew/libnx)
|
||||
2. `dkp-pacman -S switch-curl switch-freetype switch-libjpeg-turbo switch-libjson-c switch-libpng switch-libwebp switch-sdl2 switch-sdl2_gfx switch-sdl2_image switch-zlib`
|
||||
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 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.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
# <center> How to use Google Drive with JKSV </center>
|
||||
# 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.**
|
||||
|
|
@ -19,7 +23,7 @@
|
|||
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 the following screen. **Save and continue**.
|
||||
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**.
|
||||
|
|
@ -27,4 +31,28 @@
|
|||
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!
|
||||
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.
|
||||
22
inc/cfg.h
22
inc/cfg.h
|
|
@ -1,8 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
|
|
@ -17,22 +18,23 @@ namespace cfg
|
|||
void loadConfig();
|
||||
void saveConfig();
|
||||
|
||||
bool isBlacklisted(const uint64_t& tid);
|
||||
bool isBlacklisted(uint64_t tid);
|
||||
void addTitleToBlacklist(void *a);
|
||||
void removeTitleFromBlacklist(const uint64_t& tid);
|
||||
void removeTitleFromBlacklist(uint64_t tid);
|
||||
|
||||
bool isFavorite(const uint64_t& tid);
|
||||
void addTitleToFavorites(const uint64_t& tid);
|
||||
bool isFavorite(uint64_t tid);
|
||||
void addTitleToFavorites(uint64_t tid);
|
||||
|
||||
bool isDefined(const uint64_t& tid);
|
||||
void pathDefAdd(const uint64_t& tid, const std::string& newPath);
|
||||
std::string getPathDefinition(const 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(const uint64_t& tid, const std::string& _p);
|
||||
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
|
||||
|
|
|
|||
117
inc/data.h
117
inc/data.h
|
|
@ -1,87 +1,115 @@
|
|||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
#define BLD_MON 02
|
||||
#define BLD_DAY 12
|
||||
#define BLD_YEAR 2023
|
||||
#define BLD_MON 5
|
||||
#define BLD_DAY 28
|
||||
#define BLD_YEAR 2025
|
||||
|
||||
namespace data
|
||||
{
|
||||
//Loads user + title info
|
||||
// Loads user + title info
|
||||
void init();
|
||||
void exit();
|
||||
bool loadUsersTitles(bool clearUsers);
|
||||
void sortUserTitles();
|
||||
|
||||
//Draws some stats to the upper left corner
|
||||
// Draws some stats to the upper left corner
|
||||
void dispStats();
|
||||
|
||||
//Global stuff for all titles/saves
|
||||
// Global stuff for all titles/saves
|
||||
typedef struct
|
||||
{
|
||||
NacpStruct nacp;
|
||||
std::string title, safeTitle, author;//Shortcuts sorta.
|
||||
SDL_Texture *icon = NULL;
|
||||
bool fav;
|
||||
/// @brief Control data.
|
||||
NsApplicationControlData data;
|
||||
|
||||
/// @brief Saves whether or not the title has valid control data.
|
||||
bool hasControlData = false;
|
||||
|
||||
std::string title, safeTitle, author;
|
||||
SDL_Texture *icon = NULL;
|
||||
bool fav;
|
||||
} titleInfo;
|
||||
|
||||
//Holds stuff specific to user's titles/saves
|
||||
// Holds stuff specific to user's titles/saves
|
||||
typedef struct
|
||||
{
|
||||
//Makes it easier to grab id
|
||||
uint64_t tid;
|
||||
FsSaveDataInfo saveInfo;
|
||||
PdmPlayStatistics playStats;
|
||||
// Makes it easier to grab id
|
||||
uint64_t tid;
|
||||
FsSaveDataInfo saveInfo;
|
||||
PdmPlayStatistics playStats;
|
||||
} userTitleInfo;
|
||||
|
||||
//Class to store user info + titles
|
||||
// Class to store user info + titles
|
||||
class user
|
||||
{
|
||||
public:
|
||||
user() = default;
|
||||
user(const AccountUid& _id, const std::string& _backupName, const std::string& _safeBackupName);
|
||||
user(const AccountUid& _id, const std::string& _backupName, const std::string& _safeBackupName, SDL_Texture *img);
|
||||
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);
|
||||
|
||||
//Sets ID
|
||||
void setUID(const AccountUid& _id);
|
||||
// Sets ID
|
||||
void setUID(AccountUid _id);
|
||||
|
||||
//Assigns icon
|
||||
void assignIcon(SDL_Texture *_icn) { userIcon = _icn; }
|
||||
// Assigns icon
|
||||
void assignIcon(SDL_Texture *_icn)
|
||||
{
|
||||
userIcon = _icn;
|
||||
}
|
||||
|
||||
//Returns user ID
|
||||
AccountUid getUID() const { return userID; }
|
||||
u128 getUID128() const { return uID128; }
|
||||
// Returns user ID
|
||||
AccountUid getUID() const
|
||||
{
|
||||
return userID;
|
||||
}
|
||||
u128 getUID128() const
|
||||
{
|
||||
return uID128;
|
||||
}
|
||||
|
||||
//Returns username
|
||||
std::string getUsername() const { return username; }
|
||||
std::string getUsernameSafe() const { return userSafe; }
|
||||
// Returns username
|
||||
std::string getUsername() const
|
||||
{
|
||||
return username;
|
||||
}
|
||||
std::string getUsernameSafe() const
|
||||
{
|
||||
return userSafe;
|
||||
}
|
||||
|
||||
SDL_Texture *getUserIcon(){ return userIcon; }
|
||||
void delIcon(){ SDL_DestroyTexture(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);
|
||||
void addUserTitleInfo(const uint64_t &_tid,
|
||||
const FsSaveDataInfo *_saveInfo,
|
||||
const PdmPlayStatistics *_stats);
|
||||
|
||||
private:
|
||||
AccountUid userID;
|
||||
u128 uID128;
|
||||
std::string username, userSafe;
|
||||
//User icon
|
||||
// User icon
|
||||
SDL_Texture *userIcon;
|
||||
};
|
||||
|
||||
//User vector
|
||||
// User vector
|
||||
extern std::vector<user> users;
|
||||
//Title data/info map
|
||||
// Title data/info map
|
||||
extern std::unordered_map<uint64_t, data::titleInfo> titles;
|
||||
|
||||
//Sets/Retrieves current user/title
|
||||
// Sets/Retrieves current user/title
|
||||
void setUserIndex(unsigned _sUser);
|
||||
data::user *getCurrentUser();
|
||||
unsigned getCurrentUserIndex();
|
||||
|
|
@ -90,13 +118,14 @@ namespace data
|
|||
data::userTitleInfo *getCurrentUserTitleInfo();
|
||||
unsigned getCurrentUserTitleInfoIndex();
|
||||
|
||||
//Gets pointer to info that also has title + nacp
|
||||
data::titleInfo *getTitleInfoByTID(const uint64_t& tid);
|
||||
// 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);
|
||||
// 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
|
||||
|
|
|
|||
36
inc/fs.h
36
inc/fs.h
|
|
@ -1,14 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <minizip/zip.h>
|
||||
#include <minizip/unzip.h>
|
||||
#include <minizip/zip.h>
|
||||
|
||||
#include "fs/fstype.h"
|
||||
#include "fs/file.h"
|
||||
#include "fs/dir.h"
|
||||
#include "fs/zip.h"
|
||||
#include "fs/file.h"
|
||||
#include "fs/fsfile.h"
|
||||
#include "fs/drive.h"
|
||||
#include "fs/fstype.h"
|
||||
#include "fs/remote.h"
|
||||
#include "fs/zip.h"
|
||||
#include "ui/miscui.h"
|
||||
|
||||
#define BUFF_SIZE 0x4000
|
||||
|
|
@ -17,19 +17,29 @@
|
|||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
void setWorkDir(const std::string &_w);
|
||||
|
||||
//Loads paths to filter from backup/deletion
|
||||
void loadPathFilters(const uint64_t& tid);
|
||||
bool pathIsFiltered(const std::string& _path);
|
||||
void loadPathFilters(uint64_t tid);
|
||||
bool pathIsFiltered(const std::string &_path);
|
||||
void freePathFilters();
|
||||
|
||||
void createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t);
|
||||
|
|
@ -52,4 +62,4 @@ namespace fs
|
|||
|
||||
void logOpen();
|
||||
void logWrite(const char *fmt, ...);
|
||||
}
|
||||
} // namespace fs
|
||||
|
|
|
|||
73
inc/fs/dir.h
73
inc/fs/dir.h
|
|
@ -1,31 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#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);
|
||||
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);
|
||||
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; }
|
||||
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; }
|
||||
|
||||
bool isDir() const
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string itm;
|
||||
|
|
@ -37,18 +47,37 @@ namespace fs
|
|||
{
|
||||
public:
|
||||
dirList() = default;
|
||||
dirList(const std::string& _path);
|
||||
void reassign(const std::string& _path);
|
||||
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]; }
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../gd.h"
|
||||
|
||||
#define JKSV_DRIVE_FOLDER "JKSV"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
extern drive::gd *gDrive;
|
||||
extern std::string jksvDriveID;
|
||||
|
||||
void driveInit();
|
||||
void driveExit();
|
||||
std::string driveSignInGetAuthCode();
|
||||
}
|
||||
21
inc/fs/remote.h
Normal file
21
inc/fs/remote.h
Normal 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();
|
||||
}
|
||||
24
inc/gd.h
24
inc/gd.h
|
|
@ -8,6 +8,7 @@
|
|||
#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 "
|
||||
|
|
@ -16,14 +17,7 @@
|
|||
|
||||
namespace drive
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
std::string name, id, parent;
|
||||
bool isDir = false;
|
||||
unsigned int size;
|
||||
} gdItem;
|
||||
|
||||
class gd
|
||||
class gd : public rfs::IRemoteFS
|
||||
{
|
||||
public:
|
||||
void setClientID(const std::string& _clientID) { clientID = _clientID; }
|
||||
|
|
@ -36,16 +30,17 @@ namespace drive
|
|||
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);
|
||||
void getListWithParent(const std::string& _parent, std::vector<drive::gdItem *>& _out);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
|
@ -55,16 +50,17 @@ namespace drive
|
|||
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);
|
||||
|
||||
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(); }
|
||||
drive::gdItem *getItemAt(unsigned int _ind) { return &driveList[_ind]; }
|
||||
rfs::RfsItem *getItemAt(unsigned int _ind) { return &driveList[_ind]; }
|
||||
|
||||
private:
|
||||
std::vector<gdItem> driveList;
|
||||
std::vector<rfs::RfsItem> driveList;
|
||||
std::string clientID, secretID, token, rToken;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "textureMgr.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
extern SDL_Renderer *render;
|
||||
extern gfx::textureMgr *texMgr;
|
||||
|
||||
void init();
|
||||
void exit();
|
||||
void present();
|
||||
|
||||
SDL_Texture *loadJPEGMem(const void *jpegData, size_t jpegSize);
|
||||
SDL_Texture *loadImageFile(const char *file);
|
||||
|
||||
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);
|
||||
|
|
|
|||
33
inc/gfx/textureMgr.h
Normal file
33
inc/gfx/textureMgr.h
Normal 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;
|
||||
};
|
||||
}
|
||||
53
inc/rfs.h
Normal file
53
inc/rfs.h
Normal 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);
|
||||
}
|
||||
4
inc/ui.h
4
inc/ui.h
|
|
@ -48,7 +48,6 @@ namespace ui
|
|||
inline uint64_t padKeysDown() { return padGetButtonsDown(&pad); }
|
||||
inline uint64_t padKeysHeld() { return padGetButtons(&pad); }
|
||||
inline uint64_t padKeysUp() { return padGetButtonsUp(&pad); }
|
||||
inline void touchGetXY(uint32_t *x, uint32_t *y) { *x = touchState.touches[0].x; *y = touchState.touches[0].y; }
|
||||
|
||||
inline void changeState(int newState)
|
||||
{
|
||||
|
|
@ -87,6 +86,9 @@ namespace ui
|
|||
void init();
|
||||
void exit();
|
||||
|
||||
//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);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace ui
|
|||
{
|
||||
void fmInit();
|
||||
void fmExit();
|
||||
void fmPrep(const FsSaveDataType& _type, const std::string& _dev, const std::string& _baseSDMC, bool _commit);
|
||||
void fmPrep(FsSaveDataType _type, const std::string &_dev, const std::string &_baseSDMC, bool _commit);
|
||||
void fmUpdate();
|
||||
void fmDraw(SDL_Texture *target);
|
||||
}
|
||||
} // namespace ui
|
||||
|
|
|
|||
105
inc/ui/miscui.h
105
inc/ui/miscui.h
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
|
||||
#include "type.h"
|
||||
#include "gfx.h"
|
||||
#include "type.h"
|
||||
|
||||
#define POP_FRAME_DEFAULT 130
|
||||
|
||||
|
|
@ -26,50 +26,60 @@ 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
|
||||
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;
|
||||
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;
|
||||
SDL_Texture *icn = NULL;
|
||||
std::string txt;
|
||||
int txtWidth;
|
||||
std::vector<menuOptEvent> events;
|
||||
} menuOpt;
|
||||
|
||||
class menu
|
||||
{
|
||||
public:
|
||||
menu() = default;
|
||||
menu(const int& _x, const int& _y, const int& _rW, const int& _fS, const int& _mL);
|
||||
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; }
|
||||
void setOnChangeFunc(funcPtr func)
|
||||
{
|
||||
onChange = func;
|
||||
}
|
||||
|
||||
//executed when .update() is called.
|
||||
void setCallback(funcPtr _callback, void *args) { callback = _callback; callbackArgs = args; }
|
||||
void setCallback(funcPtr _callback, void *args)
|
||||
{
|
||||
callback = _callback;
|
||||
callbackArgs = args;
|
||||
}
|
||||
|
||||
//Adds option.
|
||||
int addOpt(SDL_Texture *_icn, const std::string& add);
|
||||
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);
|
||||
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();
|
||||
|
|
@ -78,10 +88,16 @@ namespace ui
|
|||
void update();
|
||||
|
||||
//Returns selected option
|
||||
int getSelected() { return selected; }
|
||||
int getSelected()
|
||||
{
|
||||
return selected;
|
||||
}
|
||||
|
||||
//Returns menu option count
|
||||
int getCount() { return opt.size(); }
|
||||
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);
|
||||
|
|
@ -90,11 +106,17 @@ namespace ui
|
|||
void reset();
|
||||
|
||||
//Resets selected + start
|
||||
void resetSel() { selected = 0; }
|
||||
void resetSel()
|
||||
{
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
//Enables control/disables drawing select box
|
||||
void setActive(bool _set);
|
||||
bool getActive() { return isActive; }
|
||||
bool getActive()
|
||||
{
|
||||
return isActive;
|
||||
}
|
||||
|
||||
private:
|
||||
//drawing x and y + rectangle width/height. Height is calc'd with font size
|
||||
|
|
@ -118,15 +140,18 @@ namespace ui
|
|||
public:
|
||||
//Constructor. _max is the maximum value
|
||||
progBar() = default;
|
||||
progBar(const uint64_t& _max) { max = _max; }
|
||||
progBar(uint64_t _max) : max(_max) {};
|
||||
|
||||
void setMax(const uint64_t& _max) { max = _max; };
|
||||
void setMax(uint64_t _max)
|
||||
{
|
||||
max = _max;
|
||||
}
|
||||
|
||||
//Updates progress
|
||||
void update(const uint64_t& _prog);
|
||||
void update(uint64_t _prog);
|
||||
|
||||
//Draws with text at top
|
||||
void draw(const std::string& text);
|
||||
void draw(const std::string &text);
|
||||
|
||||
private:
|
||||
uint64_t max = 0, prog = 0;
|
||||
|
|
@ -135,8 +160,8 @@ namespace ui
|
|||
|
||||
typedef struct
|
||||
{
|
||||
std::string message;
|
||||
int rectWidth = 0, frames = 0, y = 720;
|
||||
std::string message;
|
||||
int rectWidth = 0, frames = 0, y = 720;
|
||||
} popMessage;
|
||||
|
||||
class popMessageMngr
|
||||
|
|
@ -146,20 +171,26 @@ namespace ui
|
|||
|
||||
void update();
|
||||
|
||||
void popMessageAdd(const std::string& mess, int frameTime);
|
||||
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>
|
||||
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, ...);
|
||||
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);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ namespace ui
|
|||
{
|
||||
public:
|
||||
slideOutPanel(int _w, int _h, int _y, slidePanelOrientation _side, funcPtr _draw);
|
||||
~slideOutPanel();
|
||||
|
||||
void resizePanel(int _w, int _h, int _y);
|
||||
void update();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "type.h"
|
||||
#include "data.h"
|
||||
|
|
|
|||
54
inc/util.h
54
inc/util.h
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "data.h"
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
#include "gfx.h"
|
||||
#include "ui.h"
|
||||
|
||||
namespace util
|
||||
{
|
||||
|
|
@ -71,35 +71,39 @@ namespace util
|
|||
std::string getDateTime(int fmt);
|
||||
|
||||
//Copys dir list to a menu with 'D: ' + 'F: '
|
||||
void copyDirListToMenu(const 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);
|
||||
size_t getTotalPlacesInPath(const std::string& _path);
|
||||
void trimPath(std::string& _path, uint8_t _places);
|
||||
void removeLastFolderFromString(std::string &_path);
|
||||
size_t getTotalPlacesInPath(const std::string &_path);
|
||||
void trimPath(std::string &_path, uint8_t _places);
|
||||
|
||||
inline bool isASCII(const uint32_t& t)
|
||||
inline bool isASCII(uint32_t t)
|
||||
{
|
||||
return t > 30 && t < 127;
|
||||
}
|
||||
std::string safeString(const std::string& s);
|
||||
|
||||
std::string getInfoString(data::user& u, const uint64_t& tid);
|
||||
std::string safeString(const std::string &s);
|
||||
|
||||
std::string getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[]);
|
||||
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 getExtensionFromString(const std::string &get);
|
||||
std::string getFilenameFromPath(const std::string &get);
|
||||
|
||||
std::string generateAbbrev(const uint64_t& tid);
|
||||
std::string generateAbbrev(uint64_t tid);
|
||||
|
||||
//removes char from C++ string
|
||||
void stripChar(char _c, std::string& _s);
|
||||
void stripChar(char _c, std::string &_s);
|
||||
|
||||
void replaceStr(std::string& _str, const std::string& _find, const std::string& _rep);
|
||||
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);
|
||||
void replaceButtonsInString(std::string &rep);
|
||||
|
||||
//Creates a basic generic icon for stuff without one
|
||||
SDL_Texture *createIconGeneric(const char *txt, int fontSize, bool clearBack);
|
||||
|
|
@ -117,37 +121,41 @@ namespace util
|
|||
return ret;
|
||||
}
|
||||
|
||||
inline std::string getIDStr(const uint64_t& _id)
|
||||
inline std::string getIDStr(uint64_t _id)
|
||||
{
|
||||
char tmp[18];
|
||||
sprintf(tmp, "%016lX", _id);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
inline std::string getIDStrLower(const uint64_t& _id)
|
||||
inline std::string getIDStrLower(uint64_t _id)
|
||||
{
|
||||
char tmp[18];
|
||||
sprintf(tmp, "%08X", (uint32_t)_id);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
inline std::string generatePathByTID(const uint64_t& tid)
|
||||
inline std::string generatePathByTID(uint64_t tid)
|
||||
{
|
||||
return fs::getWorkDir() + data::getTitleSafeNameByTID(tid) + "/";
|
||||
}
|
||||
|
||||
std::string getSizeString(const uint64_t& _size);
|
||||
std::string getSizeString(uint64_t _size);
|
||||
|
||||
inline void createTitleDirectoryByTID(const uint64_t& tid)
|
||||
inline void createTitleDirectoryByTID(uint64_t tid)
|
||||
{
|
||||
std::string makePath = fs::getWorkDir() + data::getTitleSafeNameByTID(tid);
|
||||
mkdir(makePath.c_str(), 777);
|
||||
}
|
||||
|
||||
Result accountDeleteUser(AccountUid *uid);
|
||||
|
||||
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
53
inc/webdav.h
Normal 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);
|
||||
};
|
||||
}
|
||||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -1,43 +1,44 @@
|
|||
author = 0, "NULL"
|
||||
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 respaldo de todas las partidas guardadas en el sistema para #%s#? ¡Puede tomar un tiempo dependiendo de la cantidad de títulos!"
|
||||
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#?"
|
||||
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, "Downloading this backup from drive will overwrite the one on your SD card. Continue?"
|
||||
confirmDriveOverwrite = 0, "Al descargar este respaldo desde el servidor remoto se reemplazará el que actualmente está en la tarjeta SD, ¿Continuar?"
|
||||
#<====================================================
|
||||
|
||||
confirmOverwrite = 0, "¿Sobre escribir #%s#?"
|
||||
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, "User Count: "
|
||||
debugStatus = 1, "Current User: "
|
||||
debugStatus = 2, "Current Title: "
|
||||
debugStatus = 3, "Safe Title: "
|
||||
debugStatus = 4, "Sort Type: "
|
||||
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, "BIS: ProdInfoF"
|
||||
extrasMenu = 2, "BIS: Safe"
|
||||
extrasMenu = 3, "BIS: System"
|
||||
extrasMenu = 4, "BIS: User"
|
||||
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, "Montar 'Process RomFS'"
|
||||
extrasMenu = 9, "Navegar 'Process RomFS'"
|
||||
extrasMenu = 10, "Realizar respaldo de la carpeta de JKSV"
|
||||
extrasMenu = 11, "*[DEV]* Output en-US"
|
||||
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 "
|
||||
|
|
@ -51,12 +52,12 @@ fileModeMenuMkDir = 0, "Nuevo"
|
|||
folderMenuNew = 0, "Nuevo Respaldo"
|
||||
|
||||
#CHANGED=============================================>
|
||||
helpFolder = 0, "[A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close"
|
||||
helpFolder = 0, "[A] Selecionar [Y] Restaurar [X] Borrar [ZR] Subir [B] Cerrar"
|
||||
#<====================================================
|
||||
|
||||
helpSettings = 0, "[A] Alternar [X] Predeterminados [B] Atrás"
|
||||
helpTitle = 0, "[A] Seleccionar [L][R] Saltar [Y] Favoritos [X] Opciones de Títulos [B] Atrás"
|
||||
helpUser = 0, "[A] Seleccionar [X] Opciones de Usuario"
|
||||
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!) "
|
||||
|
|
@ -64,28 +65,31 @@ holdingText = 2, "(¡Ya Casi!) "
|
|||
#CHANGED=============================================>
|
||||
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 = 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, "Configuraciones"
|
||||
mainMenuSettings = 0, "Configs."
|
||||
onlineErrorConnecting = 0, "¡Error Conectando!"
|
||||
onlineNoUpdates = 0, "No hay actualizaciones disponibles."
|
||||
popAddedToPathFilter = 0, "se adicionó '#%s#' a filtros de carpeta."
|
||||
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, "Failed to start Google Drive."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
popDriveStarted = 0, "Google Drive started successfully."
|
||||
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!"
|
||||
|
|
@ -93,11 +97,11 @@ popFolderIsEmpty = 0, "¡La carpeta está vacía!"
|
|||
popProcessShutdown = 0, "#%s# Apagado satisfactorio."
|
||||
|
||||
#CHANGED=============================================>
|
||||
popSVIExported = 0, "SVI Exported."
|
||||
popSVIExported = 0, "SVI Exportado."
|
||||
#<====================================================
|
||||
|
||||
popSaveIsEmpty = 0, "¡Partida Guardada está vacía!"
|
||||
popTrashEmptied = 0, "Trash emptied"
|
||||
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."
|
||||
|
|
@ -106,7 +110,7 @@ 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# epandida con éxito!"
|
||||
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!"
|
||||
|
|
@ -117,7 +121,7 @@ saveDataTypeText = 3, "Datos de Dispositivo"
|
|||
saveDataTypeText = 4, "Almacenamiento Temporal"
|
||||
saveDataTypeText = 5, "Almacenamiento de Caché"
|
||||
saveDataTypeText = 6, "System BCAT"
|
||||
saveTypeMainMenu = 0, "Dispositivo"
|
||||
saveTypeMainMenu = 0, "Device"
|
||||
saveTypeMainMenu = 1, "BCAT"
|
||||
saveTypeMainMenu = 2, "Caché"
|
||||
saveTypeMainMenu = 3, "Sistema"
|
||||
|
|
@ -140,15 +144,16 @@ 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 Inglés: "
|
||||
settingsMenu = 17, "Forzar el uso de idioma Inglés: "
|
||||
settingsMenu = 18, "Habilitar papelera: "
|
||||
settingsMenu = 19, "Ordenar por : "
|
||||
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, "Alfabetico"
|
||||
sortType = 1, "Duración Juegos"
|
||||
sortType = 2, "Último Jugado"
|
||||
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"
|
||||
|
|
@ -161,13 +166,13 @@ swkbdSysSavID = 0, "Ingrese ID de Datos de Sistema"
|
|||
threadStatusAddingFileToZip = 0, "Agregando '#%s#' a archivo ZIP..."
|
||||
|
||||
#CHANGED=============================================>
|
||||
threadStatusCalculatingSaveSize = 0, "Calculating save data size..."
|
||||
threadStatusCalculatingSaveSize = 0, "Calculando tamaño de partida guardada..."
|
||||
#<====================================================
|
||||
|
||||
threadStatusCheckingForUpdate = 0, "Verificando actualizaciones..."
|
||||
|
||||
#CHANGED=============================================>
|
||||
threadStatusCompressingSaveForUpload = 0, "Compressing #%s# for upload..."
|
||||
threadStatusCompressingSaveForUpload = 0, "Comprimiendo #%s# para subirlo..."
|
||||
#<====================================================
|
||||
|
||||
threadStatusCopyingFile = 0, "Copiando '#%s#'..."
|
||||
|
|
@ -178,7 +183,7 @@ threadStatusDeletingSaveData = 0, "Borrando Partida Guardada para #%s#..."
|
|||
threadStatusDeletingUpdate = 0, "Borrando Actualización Pendiente..."
|
||||
|
||||
#CHANGED=============================================>
|
||||
threadStatusDownloadingFile = 0, "Downloading #%s#..."
|
||||
threadStatusDownloadingFile = 0, "Descargando #%s#..."
|
||||
#<====================================================
|
||||
|
||||
threadStatusDownloadingUpdate = 0, "Borrando Actualización..."
|
||||
|
|
@ -187,10 +192,10 @@ 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, "Saving the file master..."
|
||||
threadStatusSavingTranslations = 0, "Guardando archivo maestro..."
|
||||
|
||||
#CHANGED=============================================>
|
||||
threadStatusUploadingFile = 0, "Uploading #%s#..."
|
||||
threadStatusUploadingFile = 0, "Subiendo #%s#..."
|
||||
#<====================================================
|
||||
|
||||
titleOptions = 0, "Información"
|
||||
|
|
@ -203,7 +208,7 @@ titleOptions = 6, "Borrar Datos de Partida"
|
|||
titleOptions = 7, "Expandir Datos de Partida"
|
||||
|
||||
#CHANGED=============================================>
|
||||
titleOptions = 8, "Export SVI"
|
||||
titleOptions = 8, "Exportar SVI"
|
||||
#<====================================================
|
||||
|
||||
translationMainPage = 0, "Traducción: "
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -66,8 +66,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive n'est pas utilisable"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
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#? Questo può richiedere diverso tempo a seconda dei titoli trovati."
|
||||
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, "*AVVERTIMENTO*: Questo *cancellerà* il salvataggio di #%s# *dal tuo sistema*. Sei sicuro di volerlo fare?"
|
||||
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, "*AVVERTIMENTO*: Questo *resetterà* i dati di salvataggio per questo gioco come se non fosse mai stato eseguito prima. Sei sicuro di volerlo fare?"
|
||||
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: "
|
||||
|
|
@ -47,7 +47,7 @@ helpTitle = 0, "[A] Seleziona [L][R] Jump [Y] Preferiti [X] Opzioni Titolo
|
|||
helpUser = 0, "[A] Seleziona [Y] Dump di tutti i Salvataggi [X] Opzioni Utente"
|
||||
holdingText = 0, "(Tieni Premuto) "
|
||||
holdingText = 1, "(Continua a Premere) "
|
||||
holdingText = 2, "(Quasi fatto!) "
|
||||
holdingText = 2, "(Ci sei quasi!) "
|
||||
infoStatus = 0, "TID: %016lX"
|
||||
infoStatus = 1, "SID: %016lX"
|
||||
infoStatus = 2, "Tempo di Gioco: %02d:%02d"
|
||||
|
|
@ -66,8 +66,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive non è Disponibile"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -84,8 +84,10 @@ popChangeOutputFolder = 0, "#%s# から #%s# に変更"
|
|||
|
||||
#CHANGED=============================================>
|
||||
popDriveFailed = 0, "Google Driveの起動に失敗しました。"
|
||||
popDriveNotActive = 0, "Googleドライブは使用できません"
|
||||
popRemoteNotActive = 0, "Remoteドライブは使用できません"
|
||||
popDriveStarted = 0, "Google Driveが正常に起動しました。"
|
||||
popWebdavStarted = 0, "Webdav started successfully."
|
||||
popWebdavFailed =, 0, "Failed to start Webdav."
|
||||
#<====================================================
|
||||
|
||||
popErrorCommittingFile = 0, "保存するファイルのコミット中にエラーが発生しました!"
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ popCPUBoostEnabled = 0, "ZIP를 위한 CPU 부스트가 활성화되었습니다
|
|||
popChangeOutputError = 0, "#%s#에 잘못된 또는 ASCII가 아닌 문자가 포함되어 있습니다."
|
||||
popChangeOutputFolder = 0, "#%s#이(가) #%s#로 변경되었습니다."
|
||||
popDriveFailed = 0, "구글 드라이브 시작에 실패했습니다."
|
||||
popDriveNotActive = 0, "구글 드라이브를 사용할 수 없습니다."
|
||||
popRemoteNotActive = 0, "구글 드라이브를 사용할 수 없습니다."
|
||||
popDriveStarted = 0, "구글 드라이브가 성공적으로 시작되었습니다."
|
||||
popWebdavStarted = 0, "Webdav가 성공적으로 시작되었습니다."
|
||||
popWebdavFailed =, 0, "Webdav를 시작하지 못했습니다."
|
||||
popErrorCommittingFile = 0, "저장할 파일을 커밋하는 동안 오류가 발생했습니다!"
|
||||
popFolderIsEmpty = 0, "폴더가 비어 있습니다!"
|
||||
popProcessShutdown = 0, "#%s#이(가) 성공적으로 종료되었습니다."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -67,8 +67,10 @@ 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."
|
||||
popDriveNotActive = 0, "Google Drive is not available"
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -66,8 +66,10 @@ popCPUBoostEnabled = 0, "为ZIP压缩启用CPU超频。"
|
|||
popChangeOutputError = 0, "#%s#包含非法或者非ASCII的字符。"
|
||||
popChangeOutputFolder = 0, "#%s#更改到#%s#"
|
||||
popDriveFailed = 0, "Google Drive启动失败。"
|
||||
popDriveNotActive = 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#已成功关闭。"
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ infoStatus = 0, "TID: %016lX"
|
|||
infoStatus = 1, "SID: %016lX"
|
||||
infoStatus = 2, "遊玩時間: %02d:%02d"
|
||||
infoStatus = 3, "啟動次數: %u"
|
||||
infoStatus = 4, "Publisher: %s"
|
||||
infoStatus = 5, "Save Type: %s"
|
||||
infoStatus = 4, "發行商: %s"
|
||||
infoStatus = 5, "存檔類型: %s"
|
||||
infoStatus = 6, "快取索引: %u"
|
||||
infoStatus = 7, "使用者: %s"
|
||||
loadingStartPage = 0, "加載中…"
|
||||
|
|
@ -66,14 +66,16 @@ popCPUBoostEnabled = 0, "壓縮ZIP時將啟用CPU超頻."
|
|||
popChangeOutputError = 0, "#%s# 包含非法或者非ASCII字元."
|
||||
popChangeOutputFolder = 0, "#%s# 更改到 #%s#"
|
||||
popDriveFailed = 0, "無法啟用Google雲端硬碟."
|
||||
popDriveNotActive = 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, "Trash emptied"
|
||||
popTrashEmptied = 0, "資源回收筒已清空"
|
||||
popZipIsEmpty = 0, "ZIP壓縮檔內沒有檔案!"
|
||||
saveDataBackupDeleted = 0, "#%s#已被刪除."
|
||||
saveDataBackupMovedToTrash = 0, "#%s#已被移動至資源回收筒."
|
||||
|
|
|
|||
261
src/cfg.cpp
261
src/cfg.cpp
|
|
@ -1,14 +1,14 @@
|
|||
#include <switch.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#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"
|
||||
#include "type.h"
|
||||
|
||||
std::unordered_map<std::string, bool> cfg::config;
|
||||
std::vector<uint64_t> cfg::blacklist;
|
||||
|
|
@ -16,22 +16,41 @@ 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 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(const uint64_t& tid)
|
||||
bool cfg::isBlacklisted(uint64_t tid)
|
||||
{
|
||||
for(uint64_t& bid : cfg::blacklist)
|
||||
if(tid == bid) return true;
|
||||
for (uint64_t &bid : cfg::blacklist)
|
||||
if (tid == bid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -43,47 +62,51 @@ void cfg::addTitleToBlacklist(void *a)
|
|||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
uint64_t tid = d->tid;
|
||||
cfg::blacklist.push_back(tid);
|
||||
for(data::user& u : data::users)
|
||||
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);
|
||||
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(const uint64_t& tid)
|
||||
void cfg::removeTitleFromBlacklist(uint64_t tid)
|
||||
{
|
||||
for(unsigned i = 0; i < cfg::blacklist.size(); i++)
|
||||
for (unsigned i = 0; i < cfg::blacklist.size(); i++)
|
||||
{
|
||||
if(cfg::blacklist[i] == tid)
|
||||
if (cfg::blacklist[i] == tid)
|
||||
cfg::blacklist.erase(cfg::blacklist.begin() + i);
|
||||
}
|
||||
data::loadUsersTitles(false);
|
||||
ui::ttlRefresh();
|
||||
cfg::saveConfig();
|
||||
}
|
||||
|
||||
bool cfg::isFavorite(const uint64_t& tid)
|
||||
bool cfg::isFavorite(uint64_t tid)
|
||||
{
|
||||
for(uint64_t& fid : cfg::favorites)
|
||||
if(tid == fid) return true;
|
||||
for (uint64_t &fid : cfg::favorites)
|
||||
if (tid == fid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int getFavoriteIndex(const uint64_t& tid)
|
||||
static int getFavoriteIndex(uint64_t tid)
|
||||
{
|
||||
for(unsigned i = 0; i < cfg::favorites.size(); i++)
|
||||
for (unsigned i = 0; i < cfg::favorites.size(); i++)
|
||||
{
|
||||
if(tid == cfg::favorites[i])
|
||||
if (tid == cfg::favorites[i])
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void cfg::addTitleToFavorites(const uint64_t& tid)
|
||||
void cfg::addTitleToFavorites(uint64_t tid)
|
||||
{
|
||||
if(cfg::isFavorite(tid))
|
||||
if (cfg::isFavorite(tid))
|
||||
{
|
||||
int rem = getFavoriteIndex(tid);
|
||||
cfg::favorites.erase(cfg::favorites.begin() + rem);
|
||||
|
|
@ -93,22 +116,24 @@ void cfg::addTitleToFavorites(const uint64_t& tid)
|
|||
|
||||
data::sortUserTitles();
|
||||
ui::ttlRefresh();
|
||||
cfg::saveConfig();
|
||||
}
|
||||
|
||||
bool cfg::isDefined(const uint64_t& tid)
|
||||
bool cfg::isDefined(uint64_t tid)
|
||||
{
|
||||
for(auto& def : pathDefs)
|
||||
if(def.first == tid) return true;
|
||||
for (auto &def : pathDefs)
|
||||
if (def.first == tid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void cfg::pathDefAdd(const uint64_t& tid, const std::string& newPath)
|
||||
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())
|
||||
if (!tmp.empty())
|
||||
{
|
||||
pathDefs[tid] = tmp;
|
||||
t->safeTitle = tmp;
|
||||
|
|
@ -117,18 +142,21 @@ void cfg::pathDefAdd(const uint64_t& tid, const std::string& newPath)
|
|||
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());
|
||||
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(const uint64_t& tid)
|
||||
std::string cfg::getPathDefinition(uint64_t tid)
|
||||
{
|
||||
return pathDefs[tid];
|
||||
}
|
||||
|
||||
void cfg::addPathToFilter(const uint64_t& tid, const std::string& _p)
|
||||
void cfg::addPathToFilter(uint64_t tid, const std::string &_p)
|
||||
{
|
||||
char outpath[128];
|
||||
sprintf(outpath, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
|
||||
|
|
@ -149,15 +177,16 @@ void cfg::resetConfig()
|
|||
cfg::config["accSysSave"] = false;
|
||||
cfg::config["sysSaveWrite"] = false;
|
||||
cfg::config["directFsCmd"] = false;
|
||||
cfg::config["zip"] = 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)
|
||||
static inline bool textToBool(const std::string &_txt)
|
||||
{
|
||||
return _txt == _true_ ? true : false;
|
||||
}
|
||||
|
|
@ -169,7 +198,7 @@ static inline std::string boolToText(bool _b)
|
|||
|
||||
static inline std::string sortTypeText()
|
||||
{
|
||||
switch(cfg::sortType)
|
||||
switch (cfg::sortType)
|
||||
{
|
||||
case cfg::ALPHA:
|
||||
return "ALPHA";
|
||||
|
|
@ -188,7 +217,7 @@ static inline std::string sortTypeText()
|
|||
|
||||
static void loadWorkDirLegacy()
|
||||
{
|
||||
if(fs::fileExists(workDirLegacy))
|
||||
if (fs::fileExists(workDirLegacy))
|
||||
{
|
||||
char tmp[256];
|
||||
memset(tmp, 0, 256);
|
||||
|
|
@ -207,14 +236,14 @@ static void loadWorkDirLegacy()
|
|||
static void loadConfigLegacy()
|
||||
{
|
||||
std::string legacyCfgPath = fs::getWorkDir() + "cfg.bin";
|
||||
if(fs::fileExists(legacyCfgPath))
|
||||
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)
|
||||
if (ui::animScale == 0)
|
||||
ui::animScale = 3.0f;
|
||||
fclose(oldCfg);
|
||||
|
||||
|
|
@ -238,10 +267,10 @@ static void loadConfigLegacy()
|
|||
static void loadFavoritesLegacy()
|
||||
{
|
||||
std::string legacyFavPath = fs::getWorkDir() + "favorites.txt";
|
||||
if(fs::fileExists(legacyFavPath))
|
||||
if (fs::fileExists(legacyFavPath))
|
||||
{
|
||||
fs::dataFile fav(legacyFavPath);
|
||||
while(fav.readNextLine(false))
|
||||
while (fav.readNextLine(false))
|
||||
cfg::favorites.push_back(strtoul(fav.getLine().c_str(), NULL, 16));
|
||||
fav.close();
|
||||
fs::delfile(legacyFavPath);
|
||||
|
|
@ -251,10 +280,10 @@ static void loadFavoritesLegacy()
|
|||
static void loadBlacklistLegacy()
|
||||
{
|
||||
std::string legacyBlPath = fs::getWorkDir() + "blacklist.txt";
|
||||
if(fs::fileExists(legacyBlPath))
|
||||
if (fs::fileExists(legacyBlPath))
|
||||
{
|
||||
fs::dataFile bl(legacyBlPath);
|
||||
while(bl.readNextLine(false))
|
||||
while (bl.readNextLine(false))
|
||||
cfg::blacklist.push_back(strtoul(bl.getLine().c_str(), NULL, 16));
|
||||
bl.close();
|
||||
fs::delfile(legacyBlPath);
|
||||
|
|
@ -264,10 +293,10 @@ static void loadBlacklistLegacy()
|
|||
static void loadTitleDefsLegacy()
|
||||
{
|
||||
std::string titleDefLegacy = fs::getWorkDir() + "titleDefs.txt";
|
||||
if(fs::fileExists(titleDefLegacy))
|
||||
if (fs::fileExists(titleDefLegacy))
|
||||
{
|
||||
fs::dataFile getPaths(titleDefLegacy);
|
||||
while(getPaths.readNextLine(true))
|
||||
while (getPaths.readNextLine(true))
|
||||
{
|
||||
uint64_t tid = strtoul(getPaths.getName().c_str(), NULL, 16);
|
||||
pathDefs[tid] = getPaths.getNextValueStr();
|
||||
|
|
@ -280,10 +309,10 @@ static void loadTitleDefsLegacy()
|
|||
//Oops
|
||||
static void loadTitleDefs()
|
||||
{
|
||||
if(fs::fileExists(titleDefPath))
|
||||
if (fs::fileExists(titleDefPath))
|
||||
{
|
||||
fs::dataFile getPaths(titleDefPath);
|
||||
while(getPaths.readNextLine(true))
|
||||
while (getPaths.readNextLine(true))
|
||||
{
|
||||
uint64_t tid = strtoul(getPaths.getName().c_str(), NULL, 16);
|
||||
pathDefs[tid] = getPaths.getNextValueStr();
|
||||
|
|
@ -293,30 +322,60 @@ static void loadTitleDefs()
|
|||
|
||||
static void loadDriveConfig()
|
||||
{
|
||||
fs::dirList cfgList("/config/JKSV/");
|
||||
|
||||
// Start Google Drive
|
||||
fs::dirList cfgList("/config/JKSV/", true);
|
||||
std::string clientSecretPath;
|
||||
for(unsigned i = 0; i < cfgList.getCount(); i++)
|
||||
for (unsigned i = 0; i < cfgList.getCount(); i++)
|
||||
{
|
||||
std::string itemName = cfgList.getItem(i);
|
||||
if(itemName.find("client_secret") != itemName.npos)
|
||||
if (itemName.find("client_secret") != itemName.npos)
|
||||
{
|
||||
clientSecretPath = "/config/JKSV/" + cfgList.getItem(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!clientSecretPath.empty())
|
||||
if (!clientSecretPath.empty())
|
||||
{
|
||||
json_object *installed, *clientID, *clientSecret,*driveJSON = json_object_from_file(clientSecretPath.c_str());
|
||||
json_object_object_get_ex(driveJSON, "installed", &installed);
|
||||
json_object_object_get_ex(installed, "client_id", &clientID);
|
||||
json_object_object_get_ex(installed, "client_secret", &clientSecret);
|
||||
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
|
||||
|
||||
cfg::driveClientID = json_object_get_string(clientID);
|
||||
cfg::driveClientSecret = json_object_get_string(clientSecret);
|
||||
|
||||
json_object_put(driveJSON);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -330,15 +389,15 @@ void cfg::loadConfig()
|
|||
loadTitleDefsLegacy();
|
||||
loadTitleDefs();
|
||||
|
||||
if(fs::fileExists(cfgPath))
|
||||
if (fs::fileExists(cfgPath))
|
||||
{
|
||||
fs::dataFile cfgRead(cfgPath);
|
||||
while(cfgRead.readNextLine(true))
|
||||
while (cfgRead.readNextLine(true))
|
||||
{
|
||||
std::string varName = cfgRead.getName();
|
||||
if(cfgStrings.find(varName) != cfgStrings.end())
|
||||
if (cfgStrings.find(varName) != cfgStrings.end())
|
||||
{
|
||||
switch(cfgStrings[varName])
|
||||
switch (cfgStrings[varName])
|
||||
{
|
||||
case 0:
|
||||
fs::setWorkDir(cfgRead.getNextValueStr());
|
||||
|
|
@ -397,37 +456,37 @@ void cfg::loadConfig()
|
|||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
std::string tid = cfgRead.getNextValueStr();
|
||||
cfg::blacklist.push_back(strtoul(tid.c_str(), NULL, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
case 18:
|
||||
cfg::config["autoName"] = textToBool(cfgRead.getNextValueStr());
|
||||
|
|
@ -437,6 +496,10 @@ void cfg::loadConfig()
|
|||
cfg::driveRefreshToken = cfgRead.getNextValueStr();
|
||||
break;
|
||||
|
||||
case 20:
|
||||
cfg::config["autoUpload"] = textToBool(cfgRead.getNextValueStr());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -449,7 +512,7 @@ void cfg::loadConfig()
|
|||
static void savePathDefs()
|
||||
{
|
||||
FILE *pathOut = fopen(titleDefPath, "w");
|
||||
for(auto& p : pathDefs)
|
||||
for (auto &p : pathDefs)
|
||||
fprintf(pathOut, "0x%016lX = \"%s\"\n", p.first, p.second.c_str());
|
||||
fclose(pathOut);
|
||||
}
|
||||
|
|
@ -475,24 +538,24 @@ void cfg::saveConfig()
|
|||
fprintf(cfgOut, "titleSortType = %s\n", sortTypeText().c_str());
|
||||
fprintf(cfgOut, "animationScale = %f\n", ui::animScale);
|
||||
|
||||
if(!cfg::driveRefreshToken.empty())
|
||||
if (!cfg::driveRefreshToken.empty())
|
||||
fprintf(cfgOut, "driveRefreshToken = %s\n", cfg::driveRefreshToken.c_str());
|
||||
|
||||
if(!cfg::favorites.empty())
|
||||
if (!cfg::favorites.empty())
|
||||
{
|
||||
fprintf(cfgOut, "\n#favorites\n");
|
||||
for(uint64_t& f : cfg::favorites)
|
||||
for (uint64_t &f : cfg::favorites)
|
||||
fprintf(cfgOut, "favorite = 0x%016lX\n", f);
|
||||
}
|
||||
|
||||
if(!cfg::blacklist.empty())
|
||||
if (!cfg::blacklist.empty())
|
||||
{
|
||||
fprintf(cfgOut, "\n#blacklist\n");
|
||||
for(uint64_t& b : cfg::blacklist)
|
||||
for (uint64_t &b : cfg::blacklist)
|
||||
fprintf(cfgOut, "blacklist = 0x%016lX\n", b);
|
||||
}
|
||||
fclose(cfgOut);
|
||||
|
||||
if(!pathDefs.empty())
|
||||
if (!pathDefs.empty())
|
||||
savePathDefs();
|
||||
}
|
||||
|
|
|
|||
502
src/data.cpp
502
src/data.cpp
|
|
@ -1,54 +1,76 @@
|
|||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#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 "util.h"
|
||||
#include "type.h"
|
||||
#include "cfg.h"
|
||||
#include "util.h"
|
||||
|
||||
//FsSaveDataSpaceId_All doesn't work for SD
|
||||
static const unsigned saveOrder [] = { 0, 1, 2, 3, 4, 100, 101 };
|
||||
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;
|
||||
|
||||
//User vector
|
||||
/// @brief This vector holds the user instances.
|
||||
std::vector<data::user> data::users;
|
||||
|
||||
//System language
|
||||
/// @brief System language. I don't know why this is here in data?
|
||||
SetLanguage data::sysLang;
|
||||
|
||||
//For other save types
|
||||
static bool sysBCATPushed = false, tempPushed = false;
|
||||
/// @brief This is the map of titleInfo.
|
||||
std::unordered_map<uint64_t, data::titleInfo> data::titles;
|
||||
|
||||
//Sorts titles by sortType
|
||||
static struct
|
||||
{
|
||||
bool operator()(const data::userTitleInfo& a, const data::userTitleInfo& b)
|
||||
{
|
||||
//Favorites override EVERYTHING
|
||||
if(cfg::isFavorite(a.tid) != cfg::isFavorite(b.tid)) return cfg::isFavorite(a.tid);
|
||||
|
||||
switch(cfg::sortType)
|
||||
bool operator()(const data::userTitleInfo &a, const data::userTitleInfo &b)
|
||||
{
|
||||
case cfg::ALPHA:
|
||||
//Favorites override EVERYTHING
|
||||
if (cfg::isFavorite(a.tid) != cfg::isFavorite(b.tid))
|
||||
return cfg::isFavorite(a.tid);
|
||||
|
||||
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(); )
|
||||
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)
|
||||
if (pointA != pointB)
|
||||
return pointA < pointB;
|
||||
|
||||
i += aCnt;
|
||||
|
|
@ -57,115 +79,185 @@ static struct
|
|||
}
|
||||
break;
|
||||
|
||||
case cfg::MOST_PLAYED:
|
||||
return a.playStats.playtimeMinutes > b.playStats.playtimeMinutes;
|
||||
break;
|
||||
case cfg::MOST_PLAYED:
|
||||
return a.playStats.playtime > b.playStats.playtime;
|
||||
break;
|
||||
|
||||
case cfg::LAST_PLAYED:
|
||||
return a.playStats.last_timestampUser > b.playStats.last_timestampUser;
|
||||
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 AccountUid& id)
|
||||
static int getUserIndex(AccountUid id)
|
||||
{
|
||||
u128 nId = util::accountUIDToU128(id);
|
||||
for(unsigned i = 0; i < data::users.size(); i++)
|
||||
if(data::users[i].getUID128() == nId) return i;
|
||||
for (unsigned i = 0; i < data::users.size(); i++)
|
||||
if (data::users[i].getUID128() == nId)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool accountSystemSaveCheck(const FsSaveDataInfo& _inf)
|
||||
static inline bool accountSystemSaveCheck(const FsSaveDataInfo &_inf)
|
||||
{
|
||||
if(_inf.save_data_type == FsSaveDataType_System && util::accountUIDToU128(_inf.uid) != 0 && !cfg::config["accSysSave"])
|
||||
if (_inf.save_data_type == FsSaveDataType_System && util::accountUIDToU128(_inf.uid) != 0 &&
|
||||
!cfg::config["accSysSave"])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Minimal init/test to avoid loading and creating things I don't need
|
||||
static bool testMount(const FsSaveDataInfo& _inf)
|
||||
static bool testMount(const FsSaveDataInfo &_inf)
|
||||
{
|
||||
bool ret = false;
|
||||
if(!cfg::config["forceMount"])
|
||||
if (!cfg::config["forceMount"])
|
||||
return true;
|
||||
|
||||
if((ret = fs::mountSave(_inf)))
|
||||
if ((ret = fs::mountSave(_inf)))
|
||||
fs::unmountSave();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void addTitleToList(const uint64_t& tid)
|
||||
/// @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)
|
||||
{
|
||||
uint64_t outSize = 0;
|
||||
NsApplicationControlData *ctrlData = new NsApplicationControlData;
|
||||
NacpLanguageEntry *ent;
|
||||
Result ctrlRes = nsGetApplicationControlData(NsApplicationControlSource_Storage, tid, ctrlData, sizeof(NsApplicationControlData), &outSize);
|
||||
Result nacpRes = nacpGetLanguageEntry(&ctrlData->nacp, &ent);
|
||||
size_t iconSize = outSize - sizeof(ctrlData->nacp);
|
||||
// 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;
|
||||
|
||||
if(R_SUCCEEDED(ctrlRes) && !(outSize < sizeof(ctrlData->nacp)) && R_SUCCEEDED(nacpRes) && iconSize > 0)
|
||||
// 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)))
|
||||
{
|
||||
//Copy nacp
|
||||
memcpy(&data::titles[tid].nacp, &ctrlData->nacp, sizeof(NacpStruct));
|
||||
// Clear the NACP.
|
||||
std::memset(&info.data.nacp, 0x00, sizeof(NacpStruct));
|
||||
|
||||
//Setup 'shortcuts' to strings
|
||||
NacpLanguageEntry *ent;
|
||||
nacpGetLanguageEntry(&data::titles[tid].nacp, &ent);
|
||||
if(strlen(ent->name) == 0)
|
||||
data::titles[tid].title = ctrlData->nacp.lang[SetLanguage_ENUS].name;
|
||||
// Set the title and publisher.
|
||||
info.title = util::getIDStr(titleID);
|
||||
info.author = STRING_UNKOWN_AUTHOR;
|
||||
|
||||
// Check if the title has a path defined in the config.
|
||||
if (cfg::isDefined(titleID))
|
||||
{
|
||||
info.safeTitle = cfg::getPathDefinition(titleID);
|
||||
}
|
||||
else
|
||||
data::titles[tid].title = ent->name;
|
||||
data::titles[tid].author = ent->author;
|
||||
if(cfg::isDefined(tid))
|
||||
data::titles[tid].safeTitle = cfg::getPathDefinition(tid);
|
||||
else if((data::titles[tid].safeTitle = util::safeString(ent->name)) == "")
|
||||
data::titles[tid].safeTitle = util::getIDStr(tid);
|
||||
{
|
||||
info.safeTitle = util::getIDStr(titleID);
|
||||
}
|
||||
|
||||
if(cfg::isFavorite(tid))
|
||||
data::titles[tid].fav = true;
|
||||
|
||||
data::titles[tid].icon = gfx::loadJPEGMem(ctrlData->icon, iconSize);
|
||||
if(!data::titles[tid].icon)
|
||||
data::titles[tid].icon = util::createIconGeneric(util::getIDStrLower(tid).c_str(), 32, 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
|
||||
{
|
||||
memset(&data::titles[tid].nacp, 0, sizeof(NacpStruct));
|
||||
data::titles[tid].title = util::getIDStr(tid);
|
||||
data::titles[tid].author = "Someone?";
|
||||
if(cfg::isDefined(tid))
|
||||
data::titles[tid].safeTitle = cfg::getPathDefinition(tid);
|
||||
// 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
|
||||
data::titles[tid].safeTitle = util::getIDStr(tid);
|
||||
{
|
||||
info.title = entry->name;
|
||||
}
|
||||
|
||||
data::titles[tid].icon = util::createIconGeneric(util::getIDStrLower(tid).c_str(), 32, true);
|
||||
// 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;
|
||||
}
|
||||
delete ctrlData;
|
||||
}
|
||||
|
||||
static inline bool titleIsLoaded(const uint64_t& tid)
|
||||
{
|
||||
auto findTid = data::titles.find(tid);
|
||||
|
||||
return findTid == data::titles.end() ? false : true;
|
||||
}
|
||||
|
||||
static void loadUserAccounts()
|
||||
{
|
||||
s32 total = 0;
|
||||
AccountUid *uids = new AccountUid[8];
|
||||
if(R_SUCCEEDED(accountListAllUsers(uids, 8, &total)))
|
||||
// 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))
|
||||
{
|
||||
for(int i = 0; i < total; i++)
|
||||
data::users.emplace_back(uids[i], "", "");
|
||||
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], "", "");
|
||||
}
|
||||
delete[] uids;
|
||||
}
|
||||
|
||||
//This can load titles installed without having save data
|
||||
|
|
@ -173,56 +265,96 @@ static void loadTitlesFromRecords()
|
|||
{
|
||||
NsApplicationRecord nsRecord;
|
||||
int32_t entryCount = 0, recordOffset = 0;
|
||||
while(R_SUCCEEDED(nsListApplicationRecord(&nsRecord, 1, recordOffset++, &entryCount)) && entryCount > 0)
|
||||
while (R_SUCCEEDED(nsListApplicationRecord(&nsRecord, 1, recordOffset++, &entryCount)) && entryCount > 0)
|
||||
{
|
||||
if(!titleIsLoaded(nsRecord.application_id))
|
||||
addTitleToList(nsRecord.application_id);
|
||||
if (!titleIsLoaded(nsRecord.application_id))
|
||||
{
|
||||
add_title_to_list(nsRecord.application_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void importSVIs()
|
||||
static void import_svi_files(void)
|
||||
{
|
||||
std::string sviDir = fs::getWorkDir() + "svi/";
|
||||
fs::dirList sviList(sviDir);
|
||||
if(sviList.getCount() > 0)
|
||||
// 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)
|
||||
{
|
||||
for(unsigned i = 0; i < sviList.getCount(); i++)
|
||||
// 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))
|
||||
{
|
||||
uint64_t tid = 0;
|
||||
NacpStruct *nacp = new NacpStruct;
|
||||
NacpLanguageEntry *ent;
|
||||
std::string sviPath = fs::getWorkDir() + "svi/" + sviList.getItem(i);
|
||||
|
||||
size_t iconSize = fs::fsize(sviPath) - (sizeof(uint64_t) + sizeof(NacpStruct));
|
||||
uint8_t *iconBuffer = new uint8_t[iconSize];
|
||||
FILE *sviIn = fopen(sviPath.c_str(), "rb");
|
||||
fread(&tid, sizeof(uint64_t), 1, sviIn);
|
||||
fread(nacp, sizeof(NacpStruct), 1, sviIn);
|
||||
fread(iconBuffer, 1, iconSize, sviIn);
|
||||
|
||||
if(!titleIsLoaded(tid))
|
||||
{
|
||||
nacpGetLanguageEntry(nacp, &ent);
|
||||
memcpy(&data::titles[tid].nacp, nacp, sizeof(NacpStruct));
|
||||
data::titles[tid].title = ent->name;
|
||||
data::titles[tid].author = ent->author;
|
||||
if(cfg::isDefined(tid))
|
||||
data::titles[tid].safeTitle = cfg::getPathDefinition(tid);
|
||||
else if((data::titles[tid].safeTitle = util::safeString(ent->name)) == "")
|
||||
data::titles[tid].safeTitle = util::getIDStr(tid);
|
||||
|
||||
if(cfg::isFavorite(tid))
|
||||
data::titles[tid].fav = true;
|
||||
|
||||
if(!data::titles[tid].icon && iconSize > 0)
|
||||
data::titles[tid].icon = gfx::loadJPEGMem(iconBuffer, iconSize);
|
||||
else if(!data::titles[tid].icon && iconSize == 0)
|
||||
data::titles[tid].icon = util::createIconGeneric(util::getIDStrLower(tid).c_str(), 32, true);
|
||||
}
|
||||
delete nacp;
|
||||
delete[] iconBuffer;
|
||||
fclose(sviIn);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -234,21 +366,25 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
s64 total = 0;
|
||||
|
||||
loadTitlesFromRecords();
|
||||
importSVIs();
|
||||
import_svi_files();
|
||||
|
||||
//Clear titles
|
||||
for(data::user& u : data::users)
|
||||
for (data::user &u : data::users)
|
||||
{
|
||||
u.titleInfo.clear();
|
||||
if(clearUsers)
|
||||
}
|
||||
if (clearUsers)
|
||||
{
|
||||
systemUserCount = 4;
|
||||
for(data::user& u : data::users)
|
||||
for (data::user &u : data::users)
|
||||
{
|
||||
u.delIcon();
|
||||
}
|
||||
data::users.clear();
|
||||
|
||||
loadUserAccounts();
|
||||
sysBCATPushed = false;
|
||||
tempPushed = false;
|
||||
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");
|
||||
|
|
@ -256,27 +392,29 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
users.emplace_back(util::u128ToAccountUID(0), ui::getUIString("saveTypeMainMenu", 3), "System");
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 7; i++)
|
||||
for (unsigned i = 0; i < 7; i++)
|
||||
{
|
||||
if(R_FAILED(fsOpenSaveDataInfoReader(&it, (FsSaveDataSpaceId)saveOrder[i])))
|
||||
if (R_FAILED(fsOpenSaveDataInfoReader(&it, ORDER_SAVE_DATA_SPACES.at(i))))
|
||||
continue;
|
||||
|
||||
while(R_SUCCEEDED(fsSaveDataInfoReaderRead(&it, &info, 1, &total)) && total != 0)
|
||||
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)
|
||||
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))
|
||||
addTitleToList(tid);
|
||||
if (!titleIsLoaded(tid))
|
||||
{
|
||||
add_title_to_list(tid);
|
||||
}
|
||||
|
||||
//Don't bother with this stuff
|
||||
if(cfg::isBlacklisted(tid) || !accountSystemSaveCheck(info) || !testMount(info))
|
||||
if (cfg::isBlacklisted(tid) || !accountSystemSaveCheck(info) || !testMount(info))
|
||||
continue;
|
||||
|
||||
switch(info.save_data_type)
|
||||
switch (info.save_data_type)
|
||||
{
|
||||
case FsSaveDataType_Bcat:
|
||||
info.uid = util::u128ToAccountUID(2);
|
||||
|
|
@ -288,11 +426,13 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
|
||||
case FsSaveDataType_SystemBcat:
|
||||
info.uid = util::u128ToAccountUID(4);
|
||||
if(!sysBCATPushed)
|
||||
if (!s_systemBCATPushed)
|
||||
{
|
||||
++systemUserCount;
|
||||
sysBCATPushed = true;
|
||||
users.emplace_back(util::u128ToAccountUID(4), ui::getUIString("saveTypeMainMenu", 4), "System BCAT");
|
||||
s_systemBCATPushed = true;
|
||||
users.emplace_back(util::u128ToAccountUID(4),
|
||||
ui::getUIString("saveTypeMainMenu", 4),
|
||||
"System BCAT");
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -302,25 +442,30 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
|
||||
case FsSaveDataType_Temporary:
|
||||
info.uid = util::u128ToAccountUID(6);
|
||||
if(!tempPushed)
|
||||
if (!s_temporaryPushed)
|
||||
{
|
||||
++systemUserCount;
|
||||
tempPushed = true;
|
||||
users.emplace_back(util::u128ToAccountUID(6), ui::getUIString("saveTypeMainMenu", 5), "Temporary");
|
||||
s_temporaryPushed = true;
|
||||
users.emplace_back(util::u128ToAccountUID(6),
|
||||
ui::getUIString("saveTypeMainMenu", 5),
|
||||
"Temporary");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int u = getUserIndex(info.uid);
|
||||
if(u == -1)
|
||||
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);
|
||||
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);
|
||||
|
|
@ -328,15 +473,15 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
fsSaveDataInfoReaderClose(&it);
|
||||
}
|
||||
|
||||
if(cfg::config["incDev"])
|
||||
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++)
|
||||
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];
|
||||
data::user &u = data::users[i];
|
||||
u.titleInfo.insert(u.titleInfo.end(), dev.titleInfo.begin(), dev.titleInfo.end());
|
||||
}
|
||||
}
|
||||
|
|
@ -349,7 +494,7 @@ bool data::loadUsersTitles(bool clearUsers)
|
|||
void data::sortUserTitles()
|
||||
{
|
||||
|
||||
for(data::user& u : data::users)
|
||||
for (data::user &u : data::users)
|
||||
std::sort(u.titleInfo.begin(), u.titleInfo.end(), sortTitles);
|
||||
}
|
||||
|
||||
|
|
@ -360,9 +505,7 @@ void data::init()
|
|||
|
||||
void data::exit()
|
||||
{
|
||||
for(data::user& u : data::users) u.delIcon();
|
||||
for(auto& tinfo : titles)
|
||||
SDL_DestroyTexture(tinfo.second.icon);
|
||||
/*Still needed for planned future revisions*/
|
||||
}
|
||||
|
||||
void data::setUserIndex(unsigned _sUser)
|
||||
|
|
@ -395,39 +538,39 @@ unsigned data::getCurrentUserTitleInfoIndex()
|
|||
return selData;
|
||||
}
|
||||
|
||||
data::titleInfo *data::getTitleInfoByTID(const uint64_t& tid)
|
||||
data::titleInfo *data::getTitleInfoByTID(const uint64_t &tid)
|
||||
{
|
||||
if(titles.find(tid) != titles.end())
|
||||
if (titles.find(tid) != titles.end())
|
||||
return &titles[tid];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string data::getTitleNameByTID(const uint64_t& tid)
|
||||
std::string data::getTitleNameByTID(const uint64_t &tid)
|
||||
{
|
||||
return titles[tid].title;
|
||||
}
|
||||
|
||||
std::string data::getTitleSafeNameByTID(const uint64_t& tid)
|
||||
std::string data::getTitleSafeNameByTID(const uint64_t &tid)
|
||||
{
|
||||
return titles[tid].safeTitle;
|
||||
}
|
||||
|
||||
SDL_Texture *data::getTitleIconByTID(const uint64_t& tid)
|
||||
SDL_Texture *data::getTitleIconByTID(const uint64_t &tid)
|
||||
{
|
||||
return titles[tid].icon;
|
||||
}
|
||||
|
||||
int data::getTitleIndexInUser(const data::user& u, const uint64_t& tid)
|
||||
int data::getTitleIndexInUser(const data::user &u, const uint64_t &tid)
|
||||
{
|
||||
for(unsigned i = 0; i < u.titleInfo.size(); i++)
|
||||
for (unsigned i = 0; i < u.titleInfo.size(); i++)
|
||||
{
|
||||
if(u.titleInfo[i].tid == tid)
|
||||
if (u.titleInfo[i].tid == tid)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
data::user::user(const AccountUid& _id, const std::string& _backupName, const std::string& _safeBackupName)
|
||||
data::user::user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName)
|
||||
{
|
||||
userID = _id;
|
||||
uID128 = util::accountUIDToU128(_id);
|
||||
|
|
@ -435,11 +578,11 @@ data::user::user(const AccountUid& _id, const std::string& _backupName, const st
|
|||
AccountProfile prof;
|
||||
AccountProfileBase base;
|
||||
|
||||
if(R_SUCCEEDED(accountGetProfile(&prof, userID)) && R_SUCCEEDED(accountProfileGet(&prof, NULL, &base)))
|
||||
if (R_SUCCEEDED(accountGetProfile(&prof, userID)) && R_SUCCEEDED(accountProfileGet(&prof, NULL, &base)))
|
||||
{
|
||||
username = base.nickname;
|
||||
userSafe = util::safeString(username);
|
||||
if(userSafe.empty())
|
||||
if (userSafe.empty())
|
||||
{
|
||||
char tmp[32];
|
||||
sprintf(tmp, "Acc%08X", (uint32_t)uID128);
|
||||
|
|
@ -450,7 +593,7 @@ data::user::user(const AccountUid& _id, const std::string& _backupName, const st
|
|||
accountProfileGetImageSize(&prof, &jpgSize);
|
||||
uint8_t *jpegData = new uint8_t[jpgSize];
|
||||
accountProfileLoadImage(&prof, jpegData, jpgSize, &jpgSize);
|
||||
userIcon = gfx::loadJPEGMem(jpegData, jpgSize);
|
||||
userIcon = gfx::texMgr->textureLoadFromMem(IMG_FMT_JPG, jpegData, jpgSize);
|
||||
delete[] jpegData;
|
||||
|
||||
accountProfileClose(&prof);
|
||||
|
|
@ -464,20 +607,21 @@ data::user::user(const AccountUid& _id, const std::string& _backupName, const st
|
|||
titles.reserve(64);
|
||||
}
|
||||
|
||||
data::user::user(const AccountUid& _id, const std::string& _backupName, const std::string& _safeBackupName, SDL_Texture *img) : user(_id, _backupName, _safeBackupName)
|
||||
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(const AccountUid& _id)
|
||||
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)
|
||||
void data::user::addUserTitleInfo(const uint64_t &tid, const FsSaveDataInfo *_saveInfo, const PdmPlayStatistics *_stats)
|
||||
{
|
||||
data::userTitleInfo newInfo;
|
||||
newInfo.tid = tid;
|
||||
|
|
@ -486,7 +630,7 @@ void data::user::addUserTitleInfo(const uint64_t& tid, const FsSaveDataInfo *_sa
|
|||
titleInfo.push_back(newInfo);
|
||||
}
|
||||
|
||||
static const SDL_Color green = {0x00, 0xDD, 0x00, 0xFF};
|
||||
static constexpr SDL_Color green = {0x00, 0xDD, 0x00, 0xFF};
|
||||
|
||||
void data::dispStats()
|
||||
{
|
||||
|
|
@ -495,11 +639,11 @@ void data::dispStats()
|
|||
|
||||
//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)
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
382
src/fs.cpp
382
src/fs.cpp
|
|
@ -1,8 +1,8 @@
|
|||
#include <switch.h>
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "cfg.h"
|
||||
#include "fs.h"
|
||||
#include "util.h"
|
||||
|
||||
static std::string wd = "sdmc:/JKSV/";
|
||||
|
|
@ -22,65 +22,65 @@ void fs::init()
|
|||
fs::logOpen();
|
||||
}
|
||||
|
||||
bool fs::mountSave(const FsSaveDataInfo& _m)
|
||||
bool fs::mountSave(const FsSaveDataInfo &_m)
|
||||
{
|
||||
Result svOpen;
|
||||
FsSaveDataAttribute attr = {0};
|
||||
switch(_m.save_data_type)
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
{
|
||||
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;
|
||||
|
|
@ -90,11 +90,11 @@ bool fs::mountSave(const FsSaveDataInfo& _m)
|
|||
return R_SUCCEEDED(svOpen) && fsdevMountDevice("sv", sv) != -1;
|
||||
}
|
||||
|
||||
bool fs::commitToDevice(const std::string& dev)
|
||||
bool fs::commitToDevice(const std::string &dev)
|
||||
{
|
||||
bool ret = true;
|
||||
Result res = fsdevCommitDevice(dev.c_str());
|
||||
if(R_FAILED(res))
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
fs::logWrite("Error committing file to device -> 0x%X\n", res);
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popErrorCommittingFile", 0));
|
||||
|
|
@ -103,31 +103,37 @@ bool fs::commitToDevice(const std::string& dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string fs::getWorkDir() { return wd; }
|
||||
std::string fs::getWorkDir()
|
||||
{
|
||||
return wd;
|
||||
}
|
||||
|
||||
void fs::setWorkDir(const std::string& _w) { wd = _w; }
|
||||
void fs::setWorkDir(const std::string &_w)
|
||||
{
|
||||
wd = _w;
|
||||
}
|
||||
|
||||
|
||||
void fs::loadPathFilters(const uint64_t& tid)
|
||||
void fs::loadPathFilters(uint64_t tid)
|
||||
{
|
||||
char path[256];
|
||||
sprintf(path, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
|
||||
if(fs::fileExists(path))
|
||||
if (fs::fileExists(path))
|
||||
{
|
||||
fs::dataFile filter(path);
|
||||
while(filter.readNextLine(false))
|
||||
while (filter.readNextLine(false))
|
||||
pathFilter.push_back(filter.getLine());
|
||||
}
|
||||
}
|
||||
|
||||
bool fs::pathIsFiltered(const std::string& _path)
|
||||
bool fs::pathIsFiltered(const std::string &_path)
|
||||
{
|
||||
if(pathFilter.empty())
|
||||
if (pathFilter.empty())
|
||||
return false;
|
||||
|
||||
for(std::string& _p : pathFilter)
|
||||
for (std::string &_p : pathFilter)
|
||||
{
|
||||
if(_path == _p)
|
||||
if (_path == _p)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -142,16 +148,18 @@ void fs::freePathFilters()
|
|||
void fs::createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t)
|
||||
{
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(_tid);
|
||||
if(t)
|
||||
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())
|
||||
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())
|
||||
else if (_type == FsSaveDataType_Cache && indexStr.empty())
|
||||
{
|
||||
if(t)
|
||||
if (t)
|
||||
t->finished = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -168,54 +176,72 @@ void fs::createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, th
|
|||
FsSaveDataCreationInfo crt;
|
||||
memset(&crt, 0, sizeof(FsSaveDataCreationInfo));
|
||||
int64_t saveSize = 0, journalSize = 0;
|
||||
switch(_type)
|
||||
|
||||
// Grab a pointer to the nacp of the title data.
|
||||
NacpStruct *nacp = &tinfo->data.nacp;
|
||||
|
||||
switch (_type)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
saveSize = tinfo->nacp.user_account_save_data_size;
|
||||
journalSize = tinfo->nacp.user_account_save_data_journal_size;
|
||||
break;
|
||||
{
|
||||
saveSize = nacp->user_account_save_data_size;
|
||||
journalSize = nacp->user_account_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
saveSize = tinfo->nacp.device_save_data_size;
|
||||
journalSize = tinfo->nacp.device_save_data_journal_size;
|
||||
break;
|
||||
{
|
||||
saveSize = nacp->device_save_data_size;
|
||||
journalSize = nacp->device_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
saveSize = tinfo->nacp.bcat_delivery_cache_storage_size;
|
||||
journalSize = tinfo->nacp.bcat_delivery_cache_storage_size;
|
||||
break;
|
||||
{
|
||||
saveSize = nacp->bcat_delivery_cache_storage_size;
|
||||
journalSize = nacp->bcat_delivery_cache_storage_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Cache:
|
||||
{
|
||||
saveSize = 32 * 1024 * 1024;
|
||||
if(tinfo->nacp.cache_storage_journal_size > tinfo->nacp.cache_storage_data_and_journal_size_max)
|
||||
journalSize = tinfo->nacp.cache_storage_journal_size;
|
||||
if (nacp->cache_storage_journal_size > nacp->cache_storage_data_and_journal_size_max)
|
||||
{
|
||||
journalSize = nacp->cache_storage_journal_size;
|
||||
}
|
||||
else
|
||||
journalSize = tinfo->nacp.cache_storage_data_and_journal_size_max;
|
||||
break;
|
||||
{
|
||||
journalSize = nacp->cache_storage_data_and_journal_size_max;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if(t)
|
||||
{
|
||||
if (t)
|
||||
t->finished = true;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
crt.save_data_size = saveSize;
|
||||
crt.journal_size = journalSize;
|
||||
crt.available_size = 0x4000;
|
||||
crt.owner_id = _type == FsSaveDataType_Bcat ? 0x010000000000000C : tinfo->nacp.save_data_owner_id;
|
||||
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)
|
||||
if (_type != FsSaveDataType_Bcat)
|
||||
{
|
||||
meta.size = 0x40060;
|
||||
meta.type = FsSaveDataMetaType_Thumbnail;
|
||||
}
|
||||
|
||||
Result res = 0;
|
||||
if(R_SUCCEEDED(res = fsCreateSaveDataFileSystem(&attr, &crt, &meta)))
|
||||
if (R_SUCCEEDED(res = fsCreateSaveDataFileSystem(&attr, &crt, &meta)))
|
||||
{
|
||||
util::createTitleDirectoryByTID(_tid);
|
||||
data::loadUsersTitles(false);
|
||||
|
|
@ -248,14 +274,15 @@ void fs::createSaveDataThreaded(FsSaveDataType _type, uint64_t _tid, AccountUid
|
|||
|
||||
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());
|
||||
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;
|
||||
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))))
|
||||
if (R_FAILED((res = fsExtendSaveDataFileSystem(space, saveID, extSize, journal))))
|
||||
{
|
||||
int64_t totalSize = 0;
|
||||
fs::mountSave(tinfo->saveInfo);
|
||||
|
|
@ -290,30 +317,48 @@ uint64_t fs::getJournalSize(const data::userTitleInfo *tinfo)
|
|||
{
|
||||
uint64_t ret = 0;
|
||||
data::titleInfo *t = data::getTitleInfoByTID(tinfo->tid);
|
||||
switch(tinfo->saveInfo.save_data_type)
|
||||
|
||||
// NACP pointer.
|
||||
NacpStruct *nacp = &t->data.nacp;
|
||||
|
||||
switch (tinfo->saveInfo.save_data_type)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
ret = t->nacp.user_account_save_data_journal_size;
|
||||
break;
|
||||
{
|
||||
ret = nacp->user_account_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
ret = t->nacp.device_save_data_journal_size;
|
||||
break;
|
||||
{
|
||||
ret = nacp->device_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
ret = t->nacp.bcat_delivery_cache_storage_size;
|
||||
break;
|
||||
{
|
||||
ret = nacp->bcat_delivery_cache_storage_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Cache:
|
||||
if(t->nacp.cache_storage_journal_size > 0)
|
||||
ret = t->nacp.cache_storage_journal_size;
|
||||
{
|
||||
if (nacp->cache_storage_journal_size > 0)
|
||||
{
|
||||
ret = nacp->cache_storage_journal_size;
|
||||
}
|
||||
else
|
||||
ret = t->nacp.cache_storage_data_and_journal_size_max;
|
||||
break;
|
||||
{
|
||||
ret = nacp->cache_storage_data_and_journal_size_max;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
ret = BUFF_SIZE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -322,37 +367,51 @@ uint64_t fs::getJournalSizeMax(const data::userTitleInfo *tinfo)
|
|||
{
|
||||
uint64_t ret = 0;
|
||||
data::titleInfo *extend = data::getTitleInfoByTID(tinfo->tid);
|
||||
switch(tinfo->saveInfo.save_data_type)
|
||||
|
||||
// NACP pointer
|
||||
NacpStruct *nacp = &extend->data.nacp;
|
||||
|
||||
switch (tinfo->saveInfo.save_data_type)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
if(extend->nacp.user_account_save_data_journal_size_max > extend->nacp.user_account_save_data_journal_size)
|
||||
ret = extend->nacp.user_account_save_data_journal_size_max;
|
||||
{
|
||||
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 = extend->nacp.user_account_save_data_journal_size;
|
||||
break;
|
||||
ret = nacp->user_account_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
ret = extend->nacp.bcat_delivery_cache_storage_size;
|
||||
break;
|
||||
{
|
||||
ret = nacp->bcat_delivery_cache_storage_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Cache:
|
||||
if(extend->nacp.cache_storage_data_and_journal_size_max > extend->nacp.cache_storage_journal_size)
|
||||
ret = extend->nacp.cache_storage_data_and_journal_size_max;
|
||||
{
|
||||
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 = extend->nacp.cache_storage_journal_size;
|
||||
break;
|
||||
ret = nacp->cache_storage_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
if(extend->nacp.device_save_data_journal_size_max > extend->nacp.device_save_data_journal_size)
|
||||
ret = extend->nacp.device_save_data_journal_size_max;
|
||||
{
|
||||
if (nacp->device_save_data_journal_size_max > nacp->device_save_data_journal_size)
|
||||
ret = nacp->device_save_data_journal_size_max;
|
||||
else
|
||||
ret = extend->nacp.device_save_data_journal_size;
|
||||
break;
|
||||
ret = nacp->device_save_data_journal_size;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
//will just fail
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -373,7 +432,7 @@ void fs::wipeSave()
|
|||
|
||||
void fs::createNewBackup(void *a)
|
||||
{
|
||||
if(!fs::dirNotEmpty("sv:/"))
|
||||
if (!fs::dirNotEmpty("sv:/"))
|
||||
{
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSaveIsEmpty", 0));
|
||||
return;
|
||||
|
|
@ -387,43 +446,39 @@ void fs::createNewBackup(void *a)
|
|||
|
||||
std::string out;
|
||||
|
||||
if(held & HidNpadButton_R || cfg::config["autoName"])
|
||||
if (held & HidNpadButton_R || cfg::config["autoName"])
|
||||
out = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD);
|
||||
else if(held & HidNpadButton_L)
|
||||
else if (held & HidNpadButton_L)
|
||||
out = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YDM);
|
||||
else if(held & HidNpadButton_ZL)
|
||||
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"
|
||||
};
|
||||
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())
|
||||
if (!out.empty())
|
||||
{
|
||||
std::string ext = util::getExtensionFromString(out);
|
||||
std::string path = util::generatePathByTID(d->tid) + out;
|
||||
if(cfg::config["zip"] || ext == "zip")
|
||||
if (cfg::config["zip"] || ext == "zip")
|
||||
{
|
||||
if(ext != "zip")//data::zip is on but extension is not 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
|
||||
{
|
||||
|
|
@ -440,14 +495,14 @@ 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)
|
||||
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)
|
||||
else if (!fs::isDir(*dst) && util::getExtensionFromString(*dst) == "zip" && saveHasFiles)
|
||||
{
|
||||
fs::delfile(*dst);
|
||||
zipFile zip = zipOpen64(dst->c_str(), 0);
|
||||
|
|
@ -463,34 +518,36 @@ void fs::restoreBackup(void *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"]))
|
||||
if ((utinfo->saveInfo.save_data_type != FsSaveDataType_System || cfg::config["sysSaveWrite"]))
|
||||
{
|
||||
bool saveHasFiles = fs::dirNotEmpty("sv:/");
|
||||
if(cfg::config["autoBack"] && cfg::config["zip"] && saveHasFiles)
|
||||
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";
|
||||
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)
|
||||
else if (cfg::config["autoBack"] && saveHasFiles)
|
||||
{
|
||||
std::string autoFolder = util::generatePathByTID(utinfo->tid) + "/AUTO - " + u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD) + "/";
|
||||
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))
|
||||
if (fs::isDir(*restore))
|
||||
{
|
||||
restore->append("/");
|
||||
if(fs::dirNotEmpty(*restore))
|
||||
if (fs::dirNotEmpty(*restore))
|
||||
{
|
||||
t->status->setStatus(ui::getUICString("threadStatusCalculatingSaveSize", 0));
|
||||
unsigned dirCount = 0, fileCount = 0;
|
||||
uint64_t saveSize = 0;
|
||||
int64_t availSize = 0;
|
||||
int64_t availSize = 0;
|
||||
fs::getDirProps(*restore, dirCount, fileCount, saveSize);
|
||||
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
|
||||
if((int)saveSize > availSize)
|
||||
if ((int)saveSize > availSize)
|
||||
{
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
fs::unmountSave();
|
||||
|
|
@ -504,16 +561,16 @@ void fs::restoreBackup(void *a)
|
|||
else
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popFolderIsEmpty", 0));
|
||||
}
|
||||
else if(!fs::isDir(*restore) && util::getExtensionFromString(*restore) == "zip")
|
||||
else if (!fs::isDir(*restore) && util::getExtensionFromString(*restore) == "zip")
|
||||
{
|
||||
unzFile unz = unzOpen64(restore->c_str());
|
||||
if(unz && fs::zipNotEmpty(unz))
|
||||
if (unz && fs::zipNotEmpty(unz))
|
||||
{
|
||||
t->status->setStatus(ui::getUICString("threadStatusCalculatingSaveSize", 0));
|
||||
uint64_t saveSize = fs::getZipTotalSize(unz);
|
||||
int64_t availSize = 0;
|
||||
int64_t availSize = 0;
|
||||
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
|
||||
if((int)saveSize > availSize)
|
||||
if ((int)saveSize > availSize)
|
||||
{
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
fs::unmountSave();
|
||||
|
|
@ -536,7 +593,7 @@ void fs::restoreBackup(void *a)
|
|||
fs::copyFileCommitThreaded(*restore, dstPath, "sv");
|
||||
}
|
||||
}
|
||||
if(cfg::config["autoBack"])
|
||||
if (cfg::config["autoBack"])
|
||||
ui::fldRefreshMenu();
|
||||
|
||||
delete restore;
|
||||
|
|
@ -551,7 +608,7 @@ void fs::deleteBackup(void *a)
|
|||
|
||||
t->status->setStatus(ui::getUICString("threadStatusDeletingFile", 0));
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
if(cfg::config["trashBin"])
|
||||
if (cfg::config["trashBin"])
|
||||
{
|
||||
std::string oldPath = *deletePath;
|
||||
std::string trashPath = wd + "_TRASH_/" + data::getTitleSafeNameByTID(utinfo->tid);
|
||||
|
|
@ -561,7 +618,7 @@ void fs::deleteBackup(void *a)
|
|||
rename(oldPath.c_str(), trashPath.c_str());
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupMovedToTrash", 0), backupName.c_str());
|
||||
}
|
||||
else if(fs::isDir(*deletePath))
|
||||
else if (fs::isDir(*deletePath))
|
||||
{
|
||||
*deletePath += "/";
|
||||
fs::delDir(*deletePath);
|
||||
|
|
@ -584,23 +641,25 @@ void fs::dumpAllUserSaves(void *a)
|
|||
t->argPtr = c;
|
||||
data::user *u = data::getCurrentUser();
|
||||
|
||||
for(unsigned i = 0; i < u->titleInfo.size(); i++)
|
||||
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"])
|
||||
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";
|
||||
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:/"))
|
||||
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) + "/";
|
||||
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();
|
||||
|
|
@ -617,26 +676,28 @@ void fs::dumpAllUsersAllSaves(void *a)
|
|||
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
|
||||
t->argPtr = c;
|
||||
unsigned curUser = 0;
|
||||
while(data::users[curUser].getUID128() != 2)
|
||||
while (data::users[curUser].getUID128() != 2)
|
||||
{
|
||||
data::user *u = &data::users[curUser++];
|
||||
for(unsigned i = 0; i < u->titleInfo.size(); i++)
|
||||
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"])
|
||||
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";
|
||||
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:/"))
|
||||
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) + "/";
|
||||
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();
|
||||
|
|
@ -667,4 +728,3 @@ void fs::logWrite(const char *fmt, ...)
|
|||
fsfwrite(tmp, 1, strlen(tmp), debLog);
|
||||
fsfclose(debLog);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ std::string fs::dirItem::getExt() const
|
|||
return util::getExtensionFromString(itm);
|
||||
}
|
||||
|
||||
fs::dirList::dirList(const std::string& _path)
|
||||
fs::dirList::dirList(const std::string& _path, bool ignoreDotFiles)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *ent;
|
||||
|
|
@ -228,7 +228,8 @@ fs::dirList::dirList(const std::string& _path)
|
|||
d = opendir(path.c_str());
|
||||
|
||||
while((ent = readdir(d)))
|
||||
item.emplace_back(path, ent->d_name);
|
||||
if (!ignoreDotFiles || ent->d_name[0] != '.')
|
||||
item.emplace_back(path, ent->d_name);
|
||||
|
||||
closedir(d);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
#include "fs.h"
|
||||
#include "drive.h"
|
||||
#include "cfg.h"
|
||||
#include "ui.h"
|
||||
|
||||
drive::gd *fs::gDrive = NULL;
|
||||
std::string fs::jksvDriveID;
|
||||
|
||||
void fs::driveInit()
|
||||
{
|
||||
if(cfg::driveClientID.empty() || cfg::driveClientSecret.empty())
|
||||
return;
|
||||
|
||||
bool refreshed = false, exchanged = false;
|
||||
fs::gDrive = new drive::gd;
|
||||
fs::gDrive->setClientID(cfg::driveClientID);
|
||||
fs::gDrive->setClientSecret(cfg::driveClientSecret);
|
||||
if(!cfg::driveRefreshToken.empty())
|
||||
{
|
||||
fs::gDrive->setRefreshToken(cfg::driveRefreshToken);
|
||||
refreshed = fs::gDrive->refreshToken();
|
||||
}
|
||||
|
||||
if(!refreshed)
|
||||
{
|
||||
std::string authCode = driveSignInGetAuthCode();
|
||||
exchanged = fs::gDrive->exhangeAuthCode(authCode);
|
||||
}
|
||||
|
||||
if(fs::gDrive->hasToken())
|
||||
{
|
||||
if(exchanged)
|
||||
{
|
||||
cfg::driveRefreshToken = fs::gDrive->getRefreshToken();
|
||||
cfg::saveConfig();
|
||||
}
|
||||
|
||||
fs::gDrive->driveListInit("");
|
||||
|
||||
if(!fs::gDrive->dirExists(JKSV_DRIVE_FOLDER))
|
||||
fs::gDrive->createDir(JKSV_DRIVE_FOLDER, "");
|
||||
|
||||
jksvDriveID = fs::gDrive->getDirID(JKSV_DRIVE_FOLDER);
|
||||
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveStarted", 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
delete fs::gDrive;
|
||||
fs::gDrive = NULL;
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveFailed", 0));
|
||||
}
|
||||
}
|
||||
|
||||
void fs::driveExit()
|
||||
{
|
||||
if(fs::gDrive)
|
||||
delete gDrive;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
134
src/fs/remote.cpp
Normal file
134
src/fs/remote.cpp
Normal 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));
|
||||
}
|
||||
|
|
@ -25,9 +25,6 @@ static void writeFileFromZip_t(void *a)
|
|||
std::vector<uint8_t> localBuffer;
|
||||
unsigned int written = 0, journalCount = 0;
|
||||
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
uint64_t journalSpace = fs::getJournalSize(utinfo);
|
||||
|
||||
FILE *out = fopen(in->dst.c_str(), "wb");
|
||||
while(written < in->fileSize)
|
||||
{
|
||||
|
|
@ -79,8 +76,8 @@ void fs::copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int tr
|
|||
time_t raw;
|
||||
time(&raw);
|
||||
tm *locTime = localtime(&raw);
|
||||
zip_fileinfo inf = { (unsigned)locTime->tm_sec, (unsigned)locTime->tm_min, (unsigned)locTime->tm_hour,
|
||||
(unsigned)locTime->tm_mday, (unsigned)locTime->tm_mon, (unsigned)(1900 + locTime->tm_year), 0, 0, 0 };
|
||||
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;
|
||||
|
|
@ -155,7 +152,7 @@ void fs::copyZipToDir(unzFile src, const std::string& dst, const std::string& de
|
|||
c = (fs::copyArgs *)t->argPtr;
|
||||
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
uint64_t journalSize = getJournalSize(utinfo), writeCount = 0;
|
||||
uint64_t journalSize = getJournalSize(utinfo);
|
||||
char filename[FS_MAX_PATH];
|
||||
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||
int readIn = 0;
|
||||
|
|
|
|||
135
src/gd.cpp
135
src/gd.cpp
|
|
@ -16,27 +16,12 @@ Google Drive code for JKSV.
|
|||
Still major WIP
|
||||
*/
|
||||
|
||||
#define DRIVE_UPLOAD_BUFFER_SIZE 0x8000
|
||||
#define DRIVE_DOWNLOAD_BUFFER_SIZE 0xC00000
|
||||
#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"
|
||||
#define userAgent "JKSV"
|
||||
|
||||
std::vector<uint8_t> downloadBuffer;
|
||||
|
||||
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;
|
||||
|
||||
static inline void writeDriveError(const std::string& _function, const std::string& _message)
|
||||
{
|
||||
|
|
@ -48,58 +33,8 @@ static inline void writeCurlError(const std::string& _function, int _cerror)
|
|||
fs::logWrite("Drive/%s: CURL returned error %i\n", _function.c_str(), _cerror);
|
||||
}
|
||||
|
||||
static void writeThread_t(void *a)
|
||||
{
|
||||
dlWriteThreadStruct *in = (dlWriteThreadStruct *)a;
|
||||
std::vector<uint8_t> localBuff;
|
||||
int written = 0;
|
||||
|
||||
FILE *out = fopen(in->cfa->path.c_str(), "wb");
|
||||
|
||||
while(written < in->cfa->size)
|
||||
{
|
||||
std::unique_lock<std::mutex> dataLock(in->dataLock);
|
||||
in->cond.wait(dataLock, [in]{ return in->bufferFull; });
|
||||
localBuff.clear();
|
||||
localBuff.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||
in->sharedBuffer.clear();
|
||||
in->bufferFull = false;
|
||||
dataLock.unlock();
|
||||
in->cond.notify_one();
|
||||
|
||||
written += fwrite(localBuff.data(), 1, localBuff.size(), out);
|
||||
}
|
||||
fclose(out);
|
||||
downloadBuffer.clear();
|
||||
}
|
||||
|
||||
static size_t writeDataBufferThreaded(uint8_t *buff, size_t sz, size_t cnt, void *u)
|
||||
{
|
||||
dlWriteThreadStruct *in = (dlWriteThreadStruct *)u;
|
||||
downloadBuffer.insert(downloadBuffer.end(), buff, buff + (sz * cnt));
|
||||
in->downloaded += sz * cnt;
|
||||
|
||||
if(in->downloaded == in->cfa->size || downloadBuffer.size() == DRIVE_DOWNLOAD_BUFFER_SIZE)
|
||||
{
|
||||
std::unique_lock<std::mutex> dataLock(in->dataLock);
|
||||
in->cond.wait(dataLock, [in]{ return in->bufferFull == false; });
|
||||
in->sharedBuffer.assign(downloadBuffer.begin(), downloadBuffer.end());
|
||||
downloadBuffer.clear();
|
||||
in->bufferFull = true;
|
||||
dataLock.unlock();
|
||||
in->cond.notify_one();
|
||||
}
|
||||
|
||||
if(in->cfa->o)
|
||||
*in->cfa->o = in->downloaded;
|
||||
|
||||
return sz * cnt;
|
||||
}
|
||||
|
||||
bool drive::gd::exhangeAuthCode(const std::string& _authCode)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// Header
|
||||
curl_slist *postHeader = NULL;
|
||||
postHeader = curl_slist_append(postHeader, HEADER_CONTENT_TYPE_APP_JSON);
|
||||
|
|
@ -121,7 +56,7 @@ bool drive::gd::exhangeAuthCode(const std::string& _authCode)
|
|||
std::string *jsonResp = new std::string;
|
||||
CURL *curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||
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);
|
||||
|
|
@ -140,7 +75,6 @@ bool drive::gd::exhangeAuthCode(const std::string& _authCode)
|
|||
{
|
||||
token = json_object_get_string(accessToken);
|
||||
rToken = json_object_get_string(refreshToken);
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
writeDriveError("exchangeAuthCode", jsonResp->c_str());
|
||||
|
|
@ -180,7 +114,7 @@ bool drive::gd::refreshToken()
|
|||
std::string *jsonResp = new std::string;
|
||||
CURL *curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||
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);
|
||||
|
|
@ -223,7 +157,7 @@ bool drive::gd::tokenIsValid()
|
|||
CURL *curl = curl_easy_init();
|
||||
std::string *jsonResp = new std::string;
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||
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);
|
||||
|
|
@ -255,7 +189,7 @@ static int requestList(const std::string& _url, const std::string& _token, std::
|
|||
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, userAgent);
|
||||
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);
|
||||
|
|
@ -269,7 +203,7 @@ static int requestList(const std::string& _url, const std::string& _token, std::
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void processList(const std::string& _json, std::vector<drive::gdItem>& _drvl, bool _clear)
|
||||
static void processList(const std::string& _json, std::vector<rfs::RfsItem>& _drvl, bool _clear)
|
||||
{
|
||||
if(_clear)
|
||||
_drvl.clear();
|
||||
|
|
@ -290,7 +224,7 @@ static void processList(const std::string& _json, std::vector<drive::gdItem>& _d
|
|||
json_object_object_get_ex(curFile, "size", &size);
|
||||
json_object_object_get_ex(curFile, "parents", &parentArray);
|
||||
|
||||
drive::gdItem newDirItem;
|
||||
rfs::RfsItem newDirItem;
|
||||
newDirItem.name = json_object_get_string(nameString);
|
||||
newDirItem.id = json_object_get_string(idString);
|
||||
newDirItem.size = json_object_get_int(size);
|
||||
|
|
@ -356,14 +290,14 @@ void drive::gd::driveListAppend(const std::string& _q)
|
|||
writeCurlError("driveListAppend", error);
|
||||
}
|
||||
|
||||
void drive::gd::getListWithParent(const std::string& _parent, std::vector<drive::gdItem *>& _out)
|
||||
{
|
||||
_out.clear();
|
||||
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)
|
||||
_out.push_back(&driveList[i]);
|
||||
filtered.push_back(driveList[i]);
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
void drive::gd::debugWriteList()
|
||||
|
|
@ -406,7 +340,7 @@ bool drive::gd::createDir(const std::string& _dirName, const std::string& _paren
|
|||
std::string *jsonResp = new std::string;
|
||||
CURL *curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||
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);
|
||||
|
|
@ -422,7 +356,7 @@ bool drive::gd::createDir(const std::string& _dirName, const std::string& _paren
|
|||
json_object *id;
|
||||
json_object_object_get_ex(respParse, "id", &id);
|
||||
|
||||
drive::gdItem newDir;
|
||||
rfs::RfsItem newDir;
|
||||
newDir.name = _dirName;
|
||||
newDir.id = json_object_get_string(id);
|
||||
newDir.isDir = true;
|
||||
|
|
@ -461,16 +395,6 @@ bool drive::gd::dirExists(const std::string& _dirName, const std::string& _paren
|
|||
return false;
|
||||
}
|
||||
|
||||
bool drive::gd::fileExists(const std::string& _filename)
|
||||
{
|
||||
for(unsigned i = 0; i < driveList.size(); i++)
|
||||
{
|
||||
if(!driveList[i].isDir && driveList[i].name == _filename)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool drive::gd::fileExists(const std::string& _filename, const std::string& _parent)
|
||||
{
|
||||
for(unsigned i = 0; i < driveList.size(); i++)
|
||||
|
|
@ -511,7 +435,7 @@ void drive::gd::uploadFile(const std::string& _filename, const std::string& _par
|
|||
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, userAgent);
|
||||
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);
|
||||
|
|
@ -529,7 +453,7 @@ void drive::gd::uploadFile(const std::string& _filename, const std::string& _par
|
|||
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, DRIVE_UPLOAD_BUFFER_SIZE);
|
||||
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);
|
||||
|
|
@ -541,7 +465,7 @@ void drive::gd::uploadFile(const std::string& _filename, const std::string& _par
|
|||
|
||||
if(name && id && mimeType)
|
||||
{
|
||||
drive::gdItem uploadData;
|
||||
rfs::RfsItem uploadData;
|
||||
uploadData.id = json_object_get_string(id);
|
||||
uploadData.name = json_object_get_string(name);
|
||||
uploadData.isDir = false;
|
||||
|
|
@ -580,7 +504,7 @@ void drive::gd::updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_u
|
|||
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, userAgent);
|
||||
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);
|
||||
|
|
@ -596,7 +520,7 @@ void drive::gd::updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_u
|
|||
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, DRIVE_UPLOAD_BUFFER_SIZE);
|
||||
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);
|
||||
|
|
@ -632,22 +556,23 @@ void drive::gd::downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *
|
|||
getHeaders = curl_slist_append(getHeaders, std::string(HEADER_AUTHORIZATION + token).c_str());
|
||||
|
||||
//Downloading is threaded because it's too slow otherwise
|
||||
dlWriteThreadStruct dlWrite;
|
||||
rfs::dlWriteThreadStruct dlWrite;
|
||||
dlWrite.cfa = _download;
|
||||
|
||||
Thread writeThread;
|
||||
threadCreate(&writeThread, writeThread_t, &dlWrite, NULL, 0x8000, 0x2B, 2);
|
||||
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, userAgent);
|
||||
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, writeDataBufferThreaded);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, rfs::writeDataBufferThreaded);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dlWrite);
|
||||
threadStart(&writeThread);
|
||||
int error = curl_easy_perform(curl);
|
||||
|
||||
curl_easy_perform(curl);
|
||||
|
||||
threadWaitForExit(&writeThread);
|
||||
threadClose(&writeThread);
|
||||
|
|
@ -672,10 +597,10 @@ void drive::gd::deleteFile(const std::string& _fileID)
|
|||
//Curl
|
||||
CURL *curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, delHeaders);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
int error = curl_easy_perform(curl);
|
||||
curl_easy_perform(curl);
|
||||
|
||||
for(unsigned i = 0; i < driveList.size(); i++)
|
||||
{
|
||||
|
|
@ -690,16 +615,6 @@ void drive::gd::deleteFile(const std::string& _fileID)
|
|||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
std::string drive::gd::getFileID(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::getFileID(const std::string& _name, const std::string& _parent)
|
||||
{
|
||||
for(unsigned i = 0; i < driveList.size(); i++)
|
||||
|
|
|
|||
51
src/gfx.cpp
51
src/gfx.cpp
|
|
@ -1,8 +1,8 @@
|
|||
#include <stdio.h>
|
||||
#include <map>
|
||||
#include <switch.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
static SDL_Window *wind;
|
||||
SDL_Renderer *gfx::render;
|
||||
gfx::textureMgr *gfx::texMgr;
|
||||
|
||||
static FT_Library lib;
|
||||
static FT_Face face[6];
|
||||
|
|
@ -102,17 +103,23 @@ void gfx::init()
|
|||
|
||||
SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND);
|
||||
|
||||
gfx::texMgr = new gfx::textureMgr;
|
||||
|
||||
loadSystemFont();
|
||||
|
||||
//This is to avoid blank, black glyphs
|
||||
for(unsigned i = 0x20; i < 0x7E; i++)
|
||||
gfx::drawTextf(NULL, 18, 32, 32, &ui::txtCont, "%c", i);
|
||||
}
|
||||
|
||||
void gfx::exit()
|
||||
{
|
||||
delete gfx::texMgr;
|
||||
SDL_DestroyRenderer(gfx::render);
|
||||
SDL_DestroyWindow(wind);
|
||||
IMG_Quit();
|
||||
SDL_Quit();
|
||||
freeSystemFont();
|
||||
|
||||
for(auto c : glyphCache)
|
||||
SDL_DestroyTexture(c.second.tex);
|
||||
}
|
||||
|
||||
void gfx::present()
|
||||
|
|
@ -120,37 +127,6 @@ void gfx::present()
|
|||
SDL_RenderPresent(render);
|
||||
}
|
||||
|
||||
SDL_Texture *gfx::loadJPEGMem(const void *jpegData, size_t jpegsize)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_RWops *jpeg = SDL_RWFromConstMem(jpegData, jpegsize);
|
||||
SDL_Surface *tmpSurf = IMG_LoadJPG_RW(jpeg);
|
||||
if(tmpSurf)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(render, tmpSurf);
|
||||
SDL_FreeSurface(tmpSurf);
|
||||
}
|
||||
SDL_RWclose(jpeg);
|
||||
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SDL_Texture *gfx::loadImageFile(const char *file)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_Surface *tmpSurf = IMG_Load(file);
|
||||
if(tmpSurf)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(render, tmpSurf);
|
||||
SDL_FreeSurface(tmpSurf);
|
||||
}
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void resizeFont(int sz)
|
||||
{
|
||||
for(int i = 0; i < totalFonts; i++)
|
||||
|
|
@ -197,6 +173,9 @@ static glyphData *getGlyph(uint32_t chr, int size)
|
|||
SDL_FreeSurface(tmpSurf);
|
||||
free(tmpBuff);
|
||||
|
||||
//Add it to texture manager so textures are freed on exit
|
||||
gfx::texMgr->textureAdd(tex);
|
||||
|
||||
//Add it to cache map
|
||||
glyphCache[std::make_pair(chr, size)] = {(uint16_t)bmp.width, (uint16_t)bmp.rows, (int)glyph->advance.x >> 6, glyph->bitmap_top, glyph->bitmap_left, tex};
|
||||
|
||||
|
|
|
|||
124
src/gfx/textureMgr.cpp
Normal file
124
src/gfx/textureMgr.cpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_image.h>
|
||||
|
||||
#include "gfx.h"
|
||||
|
||||
gfx::textureMgr::~textureMgr()
|
||||
{
|
||||
for(auto tex : textures)
|
||||
SDL_DestroyTexture(tex);
|
||||
}
|
||||
|
||||
void gfx::textureMgr::textureAdd(SDL_Texture *_tex)
|
||||
{
|
||||
textures.push_back(_tex);
|
||||
}
|
||||
|
||||
SDL_Texture *gfx::textureMgr::textureCreate(int _w, int _h)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
ret = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, _w, _h);
|
||||
if(ret)
|
||||
{
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
textures.push_back(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SDL_Texture *gfx::textureMgr::textureLoadFromFile(const char *_path)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_Surface *tmp = IMG_Load(_path);
|
||||
if(tmp)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(gfx::render, tmp);
|
||||
textures.push_back(ret);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SDL_Texture *loadPNGMem(const void *_dat, size_t _datSize)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_RWops *pngData = SDL_RWFromConstMem(_dat, _datSize);
|
||||
SDL_Surface *tmp = IMG_LoadPNG_RW(pngData);
|
||||
if(tmp)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(gfx::render, tmp);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
SDL_RWclose(pngData);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SDL_Texture *loadJPEGMem(const void *_dat, size_t _datSize)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_RWops *jpegData = SDL_RWFromConstMem(_dat, _datSize);
|
||||
SDL_Surface *tmp = IMG_LoadJPG_RW(jpegData);
|
||||
if(tmp)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(gfx::render, tmp);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
SDL_RWclose(jpegData);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SDL_Texture *loadBMPMem(const void *_dat, size_t _datSize)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
SDL_RWops *bmpData = SDL_RWFromConstMem(_dat, _datSize);
|
||||
SDL_Surface *tmp = IMG_LoadBMP_RW(bmpData);
|
||||
if(tmp)
|
||||
{
|
||||
ret = SDL_CreateTextureFromSurface(gfx::render, tmp);
|
||||
SDL_FreeSurface(tmp);
|
||||
SDL_SetTextureBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
SDL_RWclose(bmpData);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SDL_Texture *gfx::textureMgr::textureLoadFromMem(imgTypes _type, const void *_dat, size_t _datSize)
|
||||
{
|
||||
SDL_Texture *ret = NULL;
|
||||
switch (_type)
|
||||
{
|
||||
case IMG_FMT_PNG:
|
||||
ret = loadPNGMem(_dat, _datSize);
|
||||
break;
|
||||
|
||||
case IMG_FMT_JPG:
|
||||
ret = loadJPEGMem(_dat, _datSize);
|
||||
break;
|
||||
|
||||
case IMG_FMT_BMP:
|
||||
ret = loadBMPMem(_dat, _datSize);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ret)
|
||||
textures.push_back(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gfx::textureMgr::textureResize(SDL_Texture **_tex, int _w, int _h)
|
||||
{
|
||||
auto texIt = std::find(textures.begin(), textures.end(), *_tex);
|
||||
if(texIt != textures.end())
|
||||
{
|
||||
SDL_DestroyTexture(*texIt);
|
||||
*_tex = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, _w, _h);
|
||||
SDL_SetTextureBlendMode(*_tex, SDL_BLENDMODE_BLEND);
|
||||
*texIt = *_tex;
|
||||
}
|
||||
}
|
||||
10
src/main.cpp
10
src/main.cpp
|
|
@ -52,13 +52,15 @@ int main(int argc, const char *argv[])
|
|||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
//Drive needs config read
|
||||
fs::driveInit();
|
||||
|
||||
if(!util::isApplet())
|
||||
fs::remoteInit();
|
||||
else
|
||||
ui::showMessage(ui::getUICString("appletModeWarning", 0));
|
||||
|
||||
while(ui::runApp()){ }
|
||||
|
||||
fs::driveExit();
|
||||
fs::remoteExit();
|
||||
curl_global_cleanup();
|
||||
cfg::saveConfig();
|
||||
ui::exit();
|
||||
data::exit();
|
||||
gfx::exit();
|
||||
|
|
|
|||
51
src/rfs.cpp
Normal file
51
src/rfs.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "rfs.h"
|
||||
|
||||
std::vector<uint8_t> rfs::downloadBuffer;
|
||||
|
||||
void rfs::writeThread_t(void *a)
|
||||
{
|
||||
rfs::dlWriteThreadStruct *in = (rfs::dlWriteThreadStruct *)a;
|
||||
std::vector<uint8_t> localBuff;
|
||||
unsigned written = 0;
|
||||
|
||||
FILE *out = fopen(in->cfa->path.c_str(), "wb");
|
||||
|
||||
while(written < in->cfa->size)
|
||||
{
|
||||
std::unique_lock<std::mutex> dataLock(in->dataLock);
|
||||
in->cond.wait(dataLock, [in]{ return in->bufferFull; });
|
||||
localBuff.clear();
|
||||
localBuff.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||
in->sharedBuffer.clear();
|
||||
in->bufferFull = false;
|
||||
dataLock.unlock();
|
||||
in->cond.notify_one();
|
||||
|
||||
written += fwrite(localBuff.data(), 1, localBuff.size(), out);
|
||||
}
|
||||
fclose(out);
|
||||
rfs::downloadBuffer.clear();
|
||||
}
|
||||
|
||||
size_t rfs::writeDataBufferThreaded(uint8_t *buff, size_t sz, size_t cnt, void *u)
|
||||
{
|
||||
rfs::dlWriteThreadStruct *in = (rfs::dlWriteThreadStruct *)u;
|
||||
rfs::downloadBuffer.insert(rfs::downloadBuffer.end(), buff, buff + (sz * cnt));
|
||||
in->downloaded += sz * cnt;
|
||||
|
||||
if(in->downloaded == in->cfa->size || rfs::downloadBuffer.size() == DOWNLOAD_BUFFER_SIZE)
|
||||
{
|
||||
std::unique_lock<std::mutex> dataLock(in->dataLock);
|
||||
in->cond.wait(dataLock, [in]{ return in->bufferFull == false; });
|
||||
in->sharedBuffer.assign(rfs::downloadBuffer.begin(), rfs::downloadBuffer.end());
|
||||
rfs::downloadBuffer.clear();
|
||||
in->bufferFull = true;
|
||||
dataLock.unlock();
|
||||
in->cond.notify_one();
|
||||
}
|
||||
|
||||
if(in->cfa->o)
|
||||
*in->cfa->o = in->downloaded;
|
||||
|
||||
return sz * cnt;
|
||||
}
|
||||
69
src/ui.cpp
69
src/ui.cpp
|
|
@ -102,10 +102,10 @@ void ui::initTheme()
|
|||
|
||||
void ui::init()
|
||||
{
|
||||
mnuTopLeft = gfx::loadImageFile("romfs:/img/fb/menuTopLeft.png");
|
||||
mnuTopRight = gfx::loadImageFile("romfs:/img/fb/menuTopRight.png");
|
||||
mnuBotLeft = gfx::loadImageFile("romfs:/img/fb/menuBotLeft.png");
|
||||
mnuBotRight = gfx::loadImageFile("romfs:/img/fb/menuBotRight.png");
|
||||
mnuTopLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/menuTopLeft.png");
|
||||
mnuTopRight = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/menuTopRight.png");
|
||||
mnuBotLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/menuBotLeft.png");
|
||||
mnuBotRight = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/menuBotRight.png");
|
||||
|
||||
corePanel = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET | SDL_TEXTUREACCESS_STATIC, 1220, 559);
|
||||
SDL_SetTextureBlendMode(corePanel, SDL_BLENDMODE_BLEND);
|
||||
|
|
@ -114,36 +114,32 @@ void ui::init()
|
|||
{
|
||||
case ColorSetId_Light:
|
||||
//Dark corners
|
||||
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopLeft.png");
|
||||
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerTopRight.png");
|
||||
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotLeft.png");
|
||||
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxLght/tboxCornerBotRight.png");
|
||||
progCovLeft = gfx::loadImageFile("romfs:/img/tboxDrk/progBarCoverLeftDrk.png");
|
||||
progCovRight = gfx::loadImageFile("romfs:/img/tboxDrk/progBarCoverRightDrk.png");
|
||||
icn = gfx::loadImageFile("romfs:/img/icn/icnDrk.png");
|
||||
sideBar = gfx::loadImageFile("romfs:/img/fb/lLight.png");
|
||||
cornerTopLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/tboxCornerTopLeft.png");
|
||||
cornerTopRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/tboxCornerTopRight.png");
|
||||
cornerBottomLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/tboxCornerBotLeft.png");
|
||||
cornerBottomRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/tboxCornerBotRight.png");
|
||||
progCovLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/progBarCoverLeftDrk.png");
|
||||
progCovRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/progBarCoverRightDrk.png");
|
||||
icn = gfx::texMgr->textureLoadFromFile("romfs:/img/icn/icnDrk.png");
|
||||
sideBar = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/lLight.png");
|
||||
break;
|
||||
|
||||
default:
|
||||
//Light corners
|
||||
cornerTopLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopLeft.png");
|
||||
cornerTopRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerTopRight.png");
|
||||
cornerBottomLeft = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotLeft.png");
|
||||
cornerBottomRight = gfx::loadImageFile("romfs:/img/tboxDrk/tboxCornerBotRight.png");
|
||||
progCovLeft = gfx::loadImageFile("romfs:/img/tboxLght/progBarCoverLeftLight.png");
|
||||
progCovRight = gfx::loadImageFile("romfs:/img/tboxLght/progBarCoverRightLight.png");
|
||||
icn = gfx::loadImageFile("romfs:/img/icn/icnLght.png");
|
||||
sideBar = gfx::loadImageFile("romfs:/img/fb/lDark.png");
|
||||
cornerTopLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/tboxCornerTopLeft.png");
|
||||
cornerTopRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/tboxCornerTopRight.png");
|
||||
cornerBottomLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/tboxCornerBotLeft.png");
|
||||
cornerBottomRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxDrk/tboxCornerBotRight.png");
|
||||
progCovLeft = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/progBarCoverLeftLight.png");
|
||||
progCovRight = gfx::texMgr->textureLoadFromFile("romfs:/img/tboxLght/progBarCoverRightLight.png");
|
||||
icn = gfx::texMgr->textureLoadFromFile("romfs:/img/icn/icnLght.png");
|
||||
sideBar = gfx::texMgr->textureLoadFromFile("romfs:/img/fb/lDark.png");
|
||||
break;
|
||||
}
|
||||
|
||||
//setup pad
|
||||
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||
padInitializeDefault(&ui::pad);
|
||||
|
||||
//Setup touch
|
||||
hidInitializeTouchScreen();
|
||||
|
||||
ui::usrInit();
|
||||
ui::ttlInit();
|
||||
ui::settInit();
|
||||
|
|
@ -170,22 +166,6 @@ void ui::exit()
|
|||
|
||||
delete popMessages;
|
||||
delete threadMngr;
|
||||
|
||||
SDL_DestroyTexture(cornerTopLeft);
|
||||
SDL_DestroyTexture(cornerTopRight);
|
||||
SDL_DestroyTexture(cornerBottomLeft);
|
||||
SDL_DestroyTexture(cornerBottomRight);
|
||||
SDL_DestroyTexture(progCovLeft);
|
||||
SDL_DestroyTexture(progCovRight);
|
||||
|
||||
SDL_DestroyTexture(mnuTopLeft);
|
||||
SDL_DestroyTexture(mnuTopRight);
|
||||
SDL_DestroyTexture(mnuBotLeft);
|
||||
SDL_DestroyTexture(mnuBotRight);
|
||||
|
||||
SDL_DestroyTexture(corePanel);
|
||||
|
||||
SDL_DestroyTexture(icn);
|
||||
}
|
||||
|
||||
int ui::registerPanel(slideOutPanel *sop)
|
||||
|
|
@ -201,12 +181,11 @@ threadInfo *ui::newThread(ThreadFunc func, void *args, funcPtr _drawFunc)
|
|||
|
||||
void ui::showLoadScreen()
|
||||
{
|
||||
SDL_Texture *icon = gfx::loadImageFile("romfs:/icon.png");
|
||||
SDL_Texture *icon = gfx::texMgr->textureLoadFromFile("romfs:/icon.png");
|
||||
gfx::clearTarget(NULL, &ui::clearClr);
|
||||
gfx::texDraw(NULL, icon, 512, 232);
|
||||
gfx::drawTextf(NULL, 16, 1100, 673, &ui::txtCont, ui::getUICString("loadingStartPage", 0));
|
||||
gfx::present();
|
||||
SDL_DestroyTexture(icon);
|
||||
}
|
||||
|
||||
void ui::drawUI()
|
||||
|
|
@ -217,7 +196,11 @@ void ui::drawUI()
|
|||
gfx::drawLine(NULL, &divClr, 30, 88, 1250, 88);
|
||||
gfx::drawLine(NULL, &divClr, 30, 648, 1250, 648);
|
||||
gfx::texDraw(NULL, icn, 66, 27);
|
||||
gfx::drawTextf(NULL, 24, 130, 38, &ui::txtCont, "JKSV");
|
||||
|
||||
if(util::isApplet())
|
||||
gfx::drawTextf(NULL, 24, 130, 38, &ui::txtCont, "JKSV *APPLET MODE*");
|
||||
else
|
||||
gfx::drawTextf(NULL, 24, 130, 38, &ui::txtCont, "JKSV");
|
||||
|
||||
//Version / translation author
|
||||
gfx::drawTextf(NULL, 12, 8, 700, &ui::txtCont, "v. %02d.%02d.%04d", BLD_MON, BLD_DAY, BLD_YEAR);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <switch.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ static SDL_Texture *fldBuffer;
|
|||
static unsigned int fldGuideWidth = 0;
|
||||
static Mutex fldLock = 0;
|
||||
static std::string driveParent;
|
||||
static std::vector<drive::gdItem *> driveFldList;
|
||||
static std::vector<rfs::RfsItem> driveFldList;
|
||||
|
||||
static void fldMenuCallback(void *a)
|
||||
{
|
||||
|
|
@ -88,7 +88,6 @@ static void fldFuncUpload_t(void *a)
|
|||
fsSetPriority(FsPriority_Realtime);
|
||||
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
std::string path, tmpZip, filename;//Final path to upload from
|
||||
|
||||
if(cfg::config["ovrClk"])
|
||||
|
|
@ -127,13 +126,13 @@ static void fldFuncUpload_t(void *a)
|
|||
upload.f = fopen(path.c_str(), "rb");
|
||||
upload.o = &cpyArgs->offset;
|
||||
|
||||
if(fs::gDrive->fileExists(filename))
|
||||
if(fs::rfs->fileExists(filename, driveParent))
|
||||
{
|
||||
std::string id = fs::gDrive->getFileID(filename);
|
||||
fs::gDrive->updateFile(id, &upload);
|
||||
std::string id = fs::rfs->getFileID(filename, driveParent);
|
||||
fs::rfs->updateFile(id, &upload);
|
||||
}
|
||||
else
|
||||
fs::gDrive->uploadFile(filename, driveParent, &upload);
|
||||
fs::rfs->uploadFile(filename, driveParent, &upload);
|
||||
|
||||
fclose(upload.f);
|
||||
|
||||
|
|
@ -153,18 +152,17 @@ static void fldFuncUpload_t(void *a)
|
|||
|
||||
static void fldFuncUpload(void *a)
|
||||
{
|
||||
if(fs::gDrive)
|
||||
if(fs::rfs)
|
||||
ui::newThread(fldFuncUpload_t, a, NULL);
|
||||
else
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popDriveNotActive", 0));
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popRemoteNotActive", 0));
|
||||
}
|
||||
|
||||
static void fldFuncDownload_t(void *a)
|
||||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
drive::gdItem *in = (drive::gdItem *)t->argPtr;
|
||||
rfs::RfsItem *in = (rfs::RfsItem *)t->argPtr;
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
std::string targetPath = util::generatePathByTID(utinfo->tid) + in->name;
|
||||
t->status->setStatus(ui::getUICString("threadStatusDownloadingFile", 0), in->name.c_str());
|
||||
|
||||
|
|
@ -186,8 +184,8 @@ static void fldFuncDownload_t(void *a)
|
|||
dlFile.path = targetPath;
|
||||
dlFile.size = in->size;
|
||||
dlFile.o = &cpy->offset;
|
||||
|
||||
fs::gDrive->downloadFile(in->id, &dlFile);
|
||||
|
||||
fs::rfs->downloadFile(in->id, &dlFile);
|
||||
|
||||
//fclose(dlFile.f);
|
||||
|
||||
|
|
@ -204,9 +202,8 @@ static void fldFuncDownload_t(void *a)
|
|||
|
||||
static void fldFuncDownload(void *a)
|
||||
{
|
||||
drive::gdItem *in = (drive::gdItem *)a;
|
||||
rfs::RfsItem *in = (rfs::RfsItem *)a;
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
std::string testPath = util::generatePathByTID(utinfo->tid) + in->name;
|
||||
if(fs::fileExists(testPath))
|
||||
{
|
||||
|
|
@ -221,16 +218,16 @@ static void fldFuncDownload(void *a)
|
|||
static void fldFuncDriveDelete_t(void *a)
|
||||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
drive::gdItem *gdi = (drive::gdItem *)t->argPtr;
|
||||
rfs::RfsItem *gdi = (rfs::RfsItem *)t->argPtr;
|
||||
t->status->setStatus(ui::getUICString("threadStatusDeletingFile", 0));
|
||||
fs::gDrive->deleteFile(gdi->id);
|
||||
fs::rfs->deleteFile(gdi->id);
|
||||
ui::fldRefreshMenu();
|
||||
t->finished = true;
|
||||
}
|
||||
|
||||
static void fldFuncDriveDelete(void *a)
|
||||
{
|
||||
drive::gdItem *in = (drive::gdItem *)a;
|
||||
rfs::RfsItem *in = (rfs::RfsItem *)a;
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], fldFuncDriveDelete_t, NULL, a, ui::getUICString("confirmDelete", 0), in->name.c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
|
|
@ -238,7 +235,7 @@ static void fldFuncDriveDelete(void *a)
|
|||
static void fldFuncDriveRestore_t(void *a)
|
||||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
drive::gdItem *gdi = (drive::gdItem *)t->argPtr;
|
||||
rfs::RfsItem *gdi = (rfs::RfsItem *)t->argPtr;
|
||||
t->status->setStatus(ui::getUICString("threadStatusDownloadingFile", 0), gdi->name.c_str());
|
||||
|
||||
fs::copyArgs *cpy = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
|
||||
|
|
@ -252,7 +249,7 @@ static void fldFuncDriveRestore_t(void *a)
|
|||
dlFile.size = gdi->size;
|
||||
dlFile.o = &cpy->offset;
|
||||
|
||||
fs::gDrive->downloadFile(gdi->id, &dlFile);
|
||||
fs::rfs->downloadFile(gdi->id, &dlFile);
|
||||
|
||||
unzFile tmp = unzOpen64("sdmc:/tmp.zip");
|
||||
fs::copyZipToDir(tmp, "sv:/", "sv", t);
|
||||
|
|
@ -268,7 +265,7 @@ static void fldFuncDriveRestore_t(void *a)
|
|||
|
||||
static void fldFuncDriveRestore(void *a)
|
||||
{
|
||||
drive::gdItem *in = (drive::gdItem *)a;
|
||||
rfs::RfsItem *in = (rfs::RfsItem *)a;
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdOver"], fldFuncDriveRestore_t, NULL, a, ui::getUICString("confirmRestore", 0), in->name.c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
|
|
@ -293,7 +290,6 @@ void ui::fldExit()
|
|||
delete ui::fldPanel;
|
||||
delete fldMenu;
|
||||
delete fldList;
|
||||
SDL_DestroyTexture(fldBuffer);
|
||||
}
|
||||
|
||||
void ui::fldUpdate()
|
||||
|
|
@ -319,21 +315,21 @@ void ui::fldPopulateMenu()
|
|||
fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, NULL);
|
||||
|
||||
unsigned fldInd = 1;
|
||||
if(fs::gDrive)
|
||||
if(fs::rfs)
|
||||
{
|
||||
if(!fs::gDrive->dirExists(t->title, fs::jksvDriveID))
|
||||
fs::gDrive->createDir(t->title, fs::jksvDriveID);
|
||||
if(!fs::rfs->dirExists(t->safeTitle, fs::rfsRootID))
|
||||
fs::rfs->createDir(t->safeTitle, fs::rfsRootID);
|
||||
|
||||
driveParent = fs::gDrive->getDirID(t->title, fs::jksvDriveID);
|
||||
driveParent = fs::rfs->getDirID(t->safeTitle, fs::rfsRootID);
|
||||
driveFldList = fs::rfs->getListWithParent(driveParent);
|
||||
|
||||
fs::gDrive->getListWithParent(driveParent, driveFldList);
|
||||
for(unsigned i = 0; i < driveFldList.size(); i++, fldInd++)
|
||||
{
|
||||
fldMenu->addOpt(NULL, "[GD] " + driveFldList[i]->name);
|
||||
fldMenu->addOpt(NULL, "[R] " + driveFldList[i].name);
|
||||
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncDriveRestore, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, &driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, &driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncDriveRestore, &driveFldList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +355,6 @@ void ui::fldRefreshMenu()
|
|||
|
||||
fldMenu->reset();
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
std::string targetDir = util::generatePathByTID(utinfo->tid);
|
||||
|
||||
fldList->reassign(targetDir);
|
||||
|
|
@ -367,17 +362,17 @@ void ui::fldRefreshMenu()
|
|||
fldMenu->optAddButtonEvent(0, HidNpadButton_A, fs::createNewBackup, NULL);
|
||||
|
||||
unsigned fldInd = 1;
|
||||
if(fs::gDrive)
|
||||
if(fs::rfs)
|
||||
{
|
||||
fs::gDrive->getListWithParent(driveParent, driveFldList);
|
||||
driveFldList = fs::rfs->getListWithParent(driveParent);
|
||||
|
||||
for(unsigned i = 0; i < driveFldList.size(); i++, fldInd++)
|
||||
{
|
||||
fldMenu->addOpt(NULL, "[GD] " + driveFldList[i]->name);
|
||||
fldMenu->addOpt(NULL, "[R] " + driveFldList[i].name);
|
||||
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncDriveRestore, driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_A, fldFuncDownload, &driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_X, fldFuncDriveDelete, &driveFldList[i]);
|
||||
fldMenu->optAddButtonEvent(fldInd, HidNpadButton_Y, fldFuncDriveRestore, &driveFldList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
168
src/ui/fm.cpp
168
src/ui/fm.cpp
|
|
@ -1,11 +1,11 @@
|
|||
#include <string>
|
||||
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
#include "util.h"
|
||||
#include "fm.h"
|
||||
#include "cfg.h"
|
||||
#include "file.h"
|
||||
#include "fm.h"
|
||||
#include "type.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
|
||||
//This is going to be a mess but the old one was too.
|
||||
//It gets more complicated because of system saves and committing.
|
||||
|
|
@ -16,9 +16,9 @@
|
|||
//Struct to hold info so i don't have to if else if if if
|
||||
typedef struct
|
||||
{
|
||||
std::string *path;
|
||||
ui::menu *m;
|
||||
fs::dirList *d;
|
||||
std::string *path;
|
||||
ui::menu *m;
|
||||
fs::dirList *d;
|
||||
} menuFuncArgs;
|
||||
|
||||
static ui::slideOutPanel *devPanel, *sdPanel;
|
||||
|
|
@ -42,7 +42,7 @@ static void refreshMenu(void *a)
|
|||
|
||||
d->reassign(*ma->path);
|
||||
util::copyDirListToMenu(*d, *m);
|
||||
for(int i = 1; i < m->getCount(); i++)
|
||||
for (int i = 1; i < m->getCount(); i++)
|
||||
{
|
||||
m->optAddButtonEvent(i, HidNpadButton_A, _listFunctionA, ma);
|
||||
}
|
||||
|
|
@ -53,10 +53,10 @@ static void refreshMenu(void *a)
|
|||
static void _devMenuCallback(void *a)
|
||||
{
|
||||
menuFuncArgs *ma = (menuFuncArgs *)a;
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
if(*ma->path != dev && *ma->path != "sdmc:/")
|
||||
if (*ma->path != dev && *ma->path != "sdmc:/")
|
||||
{
|
||||
util::removeLastFolderFromString(*ma->path);
|
||||
threadInfo fake;
|
||||
|
|
@ -75,7 +75,7 @@ static void _devMenuCallback(void *a)
|
|||
|
||||
static void _devCopyMenuCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
devCopyMenu->setActive(false);
|
||||
|
|
@ -88,10 +88,10 @@ static void _devCopyMenuCallback(void *a)
|
|||
static void _sdMenuCallback(void *a)
|
||||
{
|
||||
menuFuncArgs *ma = (menuFuncArgs *)a;
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
if(*ma->path != dev && *ma->path != "sdmc:/")
|
||||
if (*ma->path != dev && *ma->path != "sdmc:/")
|
||||
{
|
||||
util::removeLastFolderFromString(*ma->path);
|
||||
threadInfo fake;
|
||||
|
|
@ -110,7 +110,7 @@ static void _sdMenuCallback(void *a)
|
|||
|
||||
static void _sdCopyMenuCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
sdCopyMenu->setActive(false);
|
||||
|
|
@ -141,13 +141,13 @@ static void _listFunctionA(void *a)
|
|||
|
||||
int sel = m->getSelected();
|
||||
bool isDir = d->isDir(sel - 2);
|
||||
if(sel == 1 && (*ma->path != dev && *ma->path != "sdmc:/"))
|
||||
if (sel == 1 && (*ma->path != dev && *ma->path != "sdmc:/"))
|
||||
{
|
||||
util::removeLastFolderFromString(*ma->path);
|
||||
d->reassign(*ma->path);
|
||||
util::copyDirListToMenu(*d, *m);
|
||||
}
|
||||
else if(sel > 1 && isDir)
|
||||
else if (sel > 1 && isDir)
|
||||
{
|
||||
std::string addToPath = d->getItem(sel - 2);
|
||||
*ma->path += addToPath + "/";
|
||||
|
|
@ -155,7 +155,7 @@ static void _listFunctionA(void *a)
|
|||
util::copyDirListToMenu(*d, *m);
|
||||
}
|
||||
|
||||
for(int i = 1; i < m->getCount(); i++)
|
||||
for (int i = 1; i < m->getCount(); i++)
|
||||
m->optAddButtonEvent(i, HidNpadButton_A, _listFunctionA, ma);
|
||||
}
|
||||
|
||||
|
|
@ -167,49 +167,49 @@ static void _copyMenuCopy_t(void *a)
|
|||
fs::dirList *d = ma->d;
|
||||
|
||||
int sel = m->getSelected();
|
||||
if(ma == devArgs)
|
||||
if (ma == devArgs)
|
||||
{
|
||||
if(sel == 0)
|
||||
if (sel == 0)
|
||||
fs::copyDirToDirThreaded(*ma->path, *sdmcArgs->path);
|
||||
else if(sel > 1 && d->isDir(sel - 2))
|
||||
else if (sel > 1 && d->isDir(sel - 2))
|
||||
{
|
||||
std::string srcPath = *ma->path + d->getItem(sel - 2) + "/";
|
||||
std::string dstPath = *sdmcArgs->path + d->getItem(sel - 2) + "/";
|
||||
mkdir(dstPath.substr(0, dstPath.length() - 1).c_str(), 777);
|
||||
fs::copyDirToDirThreaded(srcPath, dstPath);
|
||||
}
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
{
|
||||
std::string srcPath = *ma->path + d->getItem(sel - 2);
|
||||
std::string dstPath = *sdmcArgs->path + d->getItem(sel - 2);
|
||||
fs::copyFileThreaded(srcPath, dstPath);
|
||||
}
|
||||
}
|
||||
else if(ma == sdmcArgs)
|
||||
else if (ma == sdmcArgs)
|
||||
{
|
||||
//I know
|
||||
if(sel == 0)
|
||||
if (sel == 0)
|
||||
{
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::copyDirToDirCommitThreaded(*ma->path, *devArgs->path, dev);
|
||||
else
|
||||
fs::copyDirToDirThreaded(*ma->path, *devArgs->path);
|
||||
}
|
||||
else if(sel > 1 && d->isDir(sel - 2))
|
||||
else if (sel > 1 && d->isDir(sel - 2))
|
||||
{
|
||||
std::string srcPath = *ma->path + d->getItem(sel - 2) + "/";
|
||||
std::string dstPath = *devArgs->path + d->getItem(sel - 2) + "/";
|
||||
mkdir(dstPath.substr(0, dstPath.length() - 1).c_str(), 777);
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::copyDirToDirCommitThreaded(srcPath, dstPath, dev);
|
||||
else
|
||||
fs::copyDirToDirThreaded(srcPath, dstPath);
|
||||
}
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
{
|
||||
std::string srcPath = *ma->path + d->getItem(sel - 2);
|
||||
std::string dstPath = *devArgs->path + d->getItem(sel - 2);
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::copyFileCommitThreaded(srcPath, dstPath, dev);
|
||||
else
|
||||
fs::copyFileThreaded(srcPath, dstPath);
|
||||
|
|
@ -229,30 +229,36 @@ static void _copyMenuCopy(void *a)
|
|||
|
||||
std::string srcPath, dstPath;
|
||||
int sel = m->getSelected();
|
||||
if(sel == 0 && ma == devArgs)
|
||||
if (sel == 0 && ma == devArgs)
|
||||
{
|
||||
srcPath = *ma->path;
|
||||
dstPath = *sdmcArgs->path;
|
||||
}
|
||||
else if(sel > 1 && ma == devArgs)
|
||||
else if (sel > 1 && ma == devArgs)
|
||||
{
|
||||
srcPath = *ma->path + d->getItem(sel - 2);
|
||||
dstPath = *sdmcArgs->path + d->getItem(sel - 2);
|
||||
}
|
||||
else if(sel == 0 && ma == sdmcArgs)
|
||||
else if (sel == 0 && ma == sdmcArgs)
|
||||
{
|
||||
srcPath = *ma->path;
|
||||
dstPath = *devArgs->path;
|
||||
}
|
||||
else if(sel > 1 && ma == sdmcArgs)
|
||||
else if (sel > 1 && ma == sdmcArgs)
|
||||
{
|
||||
srcPath = *ma->path + d->getItem(m->getSelected() - 2);
|
||||
dstPath = *devArgs->path + d->getItem(m->getSelected() - 2);
|
||||
}
|
||||
|
||||
if(ma == devArgs || (ma == sdmcArgs && (type != FsSaveDataType_System || cfg::config["sysSaveWrite"])))
|
||||
if (ma == devArgs || (ma == sdmcArgs && (type != FsSaveDataType_System || cfg::config["sysSaveWrite"])))
|
||||
{
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(false, _copyMenuCopy_t, NULL, ma, ui::getUICString("confirmCopy", 0), srcPath.c_str(), dstPath.c_str());
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(false,
|
||||
_copyMenuCopy_t,
|
||||
NULL,
|
||||
ma,
|
||||
ui::getUICString("confirmCopy", 0),
|
||||
srcPath.c_str(),
|
||||
dstPath.c_str());
|
||||
ui::confirm(send);
|
||||
}
|
||||
}
|
||||
|
|
@ -267,39 +273,39 @@ static void _copyMenuDelete_t(void *a)
|
|||
t->status->setStatus(ui::getUICString("threadStatusDeletingFile", 0));
|
||||
|
||||
int sel = m->getSelected();
|
||||
if(ma == devArgs)
|
||||
if (ma == devArgs)
|
||||
{
|
||||
if(sel == 0 && *ma->path != "sdmc:/")
|
||||
if (sel == 0 && *ma->path != "sdmc:/")
|
||||
{
|
||||
fs::delDir(*ma->path);
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::commitToDevice(dev);
|
||||
}
|
||||
else if(sel > 1 && d->isDir(sel - 2))
|
||||
else if (sel > 1 && d->isDir(sel - 2))
|
||||
{
|
||||
std::string delPath = *ma->path + d->getItem(sel - 2) + "/";
|
||||
fs::delDir(delPath);
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::commitToDevice(dev);
|
||||
}
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
{
|
||||
std::string delPath = *ma->path + d->getItem(sel - 2);
|
||||
fs::delfile(delPath);
|
||||
if(commit)
|
||||
if (commit)
|
||||
fs::commitToDevice(dev);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sel == 0 && *ma->path != "sdmc:/")
|
||||
if (sel == 0 && *ma->path != "sdmc:/")
|
||||
fs::delDir(*ma->path);
|
||||
else if(sel > 1 && d->isDir(sel - 2))
|
||||
else if (sel > 1 && d->isDir(sel - 2))
|
||||
{
|
||||
std::string delPath = *ma->path + d->getItem(sel - 2) + "/";
|
||||
fs::delDir(delPath);
|
||||
}
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
{
|
||||
std::string delPath = *ma->path + d->getItem(sel - 2);
|
||||
fs::delfile(delPath);
|
||||
|
|
@ -319,14 +325,20 @@ static void _copyMenuDelete(void *a)
|
|||
|
||||
int sel = m->getSelected();
|
||||
std::string itmPath;
|
||||
if(sel == 0)
|
||||
if (sel == 0)
|
||||
itmPath = *ma->path;
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
itmPath = *ma->path + d->getItem(sel - 2);
|
||||
|
||||
if(ma == sdmcArgs || (ma == devArgs && (sel == 0 || sel > 1) && (type != FsSaveDataType_System || cfg::config["sysSaveWrite"])))
|
||||
if (ma == sdmcArgs ||
|
||||
(ma == devArgs && (sel == 0 || sel > 1) && (type != FsSaveDataType_System || cfg::config["sysSaveWrite"])))
|
||||
{
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(cfg::config["holdDel"], _copyMenuDelete_t, NULL, a, ui::getUICString("confirmDelete", 0), itmPath.c_str());
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(cfg::config["holdDel"],
|
||||
_copyMenuDelete_t,
|
||||
NULL,
|
||||
a,
|
||||
ui::getUICString("confirmDelete", 0),
|
||||
itmPath.c_str());
|
||||
ui::confirm(send);
|
||||
}
|
||||
}
|
||||
|
|
@ -338,13 +350,14 @@ static void _copyMenuRename(void *a)
|
|||
fs::dirList *d = ma->d;
|
||||
|
||||
int sel = m->getSelected();
|
||||
if(sel > 1)
|
||||
if (sel > 1)
|
||||
{
|
||||
std::string getNewName = util::getStringInput(SwkbdType_QWERTY, d->getItem(sel - 2), ui::getUIString("swkbdRename", 0), 64, 0, NULL);
|
||||
if(!getNewName.empty())
|
||||
std::string getNewName =
|
||||
util::getStringInput(SwkbdType_QWERTY, d->getItem(sel - 2), ui::getUIString("swkbdRename", 0), 64, 0, NULL);
|
||||
if (!getNewName.empty())
|
||||
{
|
||||
std::string prevPath = *ma->path + d->getItem(sel - 2);
|
||||
std::string newPath = *ma->path + getNewName;
|
||||
std::string newPath = *ma->path + getNewName;
|
||||
rename(prevPath.c_str(), newPath.c_str());
|
||||
}
|
||||
threadInfo fake;
|
||||
|
|
@ -358,8 +371,13 @@ static void _copyMenuRename(void *a)
|
|||
static void _copyMenuMkDir(void *a)
|
||||
{
|
||||
menuFuncArgs *ma = (menuFuncArgs *)a;
|
||||
std::string getNewFolder = util::getStringInput(SwkbdType_QWERTY, ui::getUIString("fileModeMenuMkDir", 0), ui::getUIString("swkbdMkDir", 0), 64, 0, NULL);
|
||||
if(!getNewFolder.empty())
|
||||
std::string getNewFolder = util::getStringInput(SwkbdType_QWERTY,
|
||||
ui::getUIString("fileModeMenuMkDir", 0),
|
||||
ui::getUIString("swkbdMkDir", 0),
|
||||
64,
|
||||
0,
|
||||
NULL);
|
||||
if (!getNewFolder.empty())
|
||||
{
|
||||
std::string createPath = *ma->path + getNewFolder;
|
||||
mkdir(createPath.c_str(), 777);
|
||||
|
|
@ -379,7 +397,11 @@ static void _copyMenuGetShowDirProps_t(void *a)
|
|||
uint64_t totalSize = 0;
|
||||
t->status->setStatus(ui::getUICString("threadStatusGetDirProps", 0));
|
||||
fs::getDirProps(*p, dirCount, fileCount, totalSize);
|
||||
ui::showMessage(ui::getUICString("fileModeFolderProperties", 0), p->c_str(), dirCount, fileCount, util::getSizeString(totalSize).c_str());
|
||||
ui::showMessage(ui::getUICString("fileModeFolderProperties", 0),
|
||||
p->c_str(),
|
||||
dirCount,
|
||||
fileCount,
|
||||
util::getSizeString(totalSize).c_str());
|
||||
delete p;
|
||||
t->finished = true;
|
||||
}
|
||||
|
|
@ -391,18 +413,18 @@ static void _copyMenuGetProps(void *a)
|
|||
fs::dirList *d = ma->d;
|
||||
|
||||
int sel = m->getSelected();
|
||||
if(sel == 0)
|
||||
if (sel == 0)
|
||||
{
|
||||
std::string *folderPath = new std::string(*ma->path);
|
||||
ui::newThread(_copyMenuGetShowDirProps_t, folderPath, NULL);
|
||||
}
|
||||
else if(sel > 1 && d->isDir(sel - 2))
|
||||
else if (sel > 1 && d->isDir(sel - 2))
|
||||
{
|
||||
std::string *folderPath = new std::string;
|
||||
folderPath->assign(*ma->path + d->getItem(sel - 2) + "/");
|
||||
ui::newThread(_copyMenuGetShowDirProps_t, folderPath, NULL);
|
||||
}
|
||||
else if(sel > 1)
|
||||
else if (sel > 1)
|
||||
{
|
||||
std::string filePath = *ma->path + d->getItem(sel - 2);
|
||||
fs::getShowFileProps(filePath);
|
||||
|
|
@ -412,7 +434,7 @@ static void _copyMenuGetProps(void *a)
|
|||
static void _copyMenuClose(void *a)
|
||||
{
|
||||
menuFuncArgs *ma = (menuFuncArgs *)a;
|
||||
if(ma == devArgs)
|
||||
if (ma == devArgs)
|
||||
{
|
||||
devCopyMenu->setActive(false);
|
||||
devMenu->setActive(true);
|
||||
|
|
@ -433,7 +455,7 @@ static void _devMenuAddToPathFilter(void *a)
|
|||
fs::dirList *d = ma->d;
|
||||
|
||||
int sel = m->getSelected();
|
||||
if(sel > 1)
|
||||
if (sel > 1)
|
||||
{
|
||||
data::userTitleInfo *tinfo = data::getCurrentUserTitleInfo();
|
||||
std::string filterPath = *ma->path + d->getItem(sel - 2);
|
||||
|
|
@ -461,7 +483,7 @@ void ui::fmInit()
|
|||
devCopyMenu->setActive(false);
|
||||
devCopyMenu->setCallback(_devCopyMenuCallback, NULL);
|
||||
devCopyMenu->addOpt(NULL, ui::getUIString("fileModeMenu", 0) + "SDMC");
|
||||
for(int i = 1; i < 4; i++)
|
||||
for (int i = 1; i < 4; i++)
|
||||
devCopyMenu->addOpt(NULL, ui::getUIString("fileModeMenu", i));
|
||||
//Manually do this so I can place the last option higher up
|
||||
devCopyMenu->addOpt(NULL, ui::getUIString("fileModeMenu", 6));
|
||||
|
|
@ -486,7 +508,7 @@ void ui::fmInit()
|
|||
sdCopyMenu = new ui::menu(10, 210, 268, 20, 5);
|
||||
sdCopyMenu->setActive(false);
|
||||
sdCopyMenu->setCallback(_sdCopyMenuCallback, NULL);
|
||||
for(int i = 0; i < 6; i++)
|
||||
for (int i = 0; i < 6; i++)
|
||||
sdCopyMenu->addOpt(NULL, ui::getUIString("fileModeMenu", i));
|
||||
sdCopyMenu->optAddButtonEvent(0, HidNpadButton_A, _copyMenuCopy, sdmcArgs);
|
||||
sdCopyMenu->optAddButtonEvent(1, HidNpadButton_A, _copyMenuDelete, sdmcArgs);
|
||||
|
|
@ -497,7 +519,7 @@ void ui::fmInit()
|
|||
ui::registerPanel(sdPanel);
|
||||
|
||||
devList = new fs::dirList;
|
||||
sdList = new fs::dirList;
|
||||
sdList = new fs::dirList;
|
||||
devArgs->d = devList;
|
||||
sdmcArgs->d = sdList;
|
||||
}
|
||||
|
|
@ -514,10 +536,10 @@ void ui::fmExit()
|
|||
delete sdmcArgs;
|
||||
}
|
||||
|
||||
void ui::fmPrep(const FsSaveDataType& _type, const std::string& _dev, const std::string& _baseSDMC, bool _commit)
|
||||
void ui::fmPrep(FsSaveDataType _type, const std::string &_dev, const std::string &_baseSDMC, bool _commit)
|
||||
{
|
||||
type = _type;
|
||||
dev = _dev;
|
||||
dev = _dev;
|
||||
commit = _commit;
|
||||
devPath = _dev;
|
||||
sdPath = _baseSDMC;
|
||||
|
|
@ -527,24 +549,24 @@ void ui::fmPrep(const FsSaveDataType& _type, const std::string& _dev, const std:
|
|||
devList->reassign(dev);
|
||||
sdList->reassign(sdPath);
|
||||
util::copyDirListToMenu(*devList, *devMenu);
|
||||
for(int i = 1; i < devMenu->getCount(); i++)
|
||||
for (int i = 1; i < devMenu->getCount(); i++)
|
||||
devMenu->optAddButtonEvent(i, HidNpadButton_A, _listFunctionA, devArgs);
|
||||
|
||||
util::copyDirListToMenu(*sdList, *sdMenu);
|
||||
for(int i = 1; i < sdMenu->getCount(); i++)
|
||||
for (int i = 1; i < sdMenu->getCount(); i++)
|
||||
sdMenu->optAddButtonEvent(i, HidNpadButton_A, _listFunctionA, sdmcArgs);
|
||||
}
|
||||
|
||||
void ui::fmUpdate()
|
||||
{
|
||||
//For now? Maybe forever?
|
||||
if(devMenu->getActive() || sdMenu->getActive())
|
||||
if (devMenu->getActive() || sdMenu->getActive())
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_ZL:
|
||||
case HidNpadButton_ZR:
|
||||
if(devMenu->getActive())
|
||||
if (devMenu->getActive())
|
||||
{
|
||||
devMenu->setActive(false);
|
||||
sdMenu->setActive(true);
|
||||
|
|
@ -558,15 +580,15 @@ void ui::fmUpdate()
|
|||
|
||||
case HidNpadButton_Minus:
|
||||
//Can't be 100% sure it's fs's sv
|
||||
if(dev != "sdmc:/")
|
||||
if (dev != "sdmc:/")
|
||||
fsdevUnmountDevice(dev.c_str());
|
||||
|
||||
if(ui::prevState == EX_MNU)
|
||||
if (ui::prevState == EX_MNU)
|
||||
{
|
||||
ui::usrSelPanel->openPanel();
|
||||
ui::changeState(EX_MNU);
|
||||
}
|
||||
else if(ui::prevState == TTL_SEL)
|
||||
else if (ui::prevState == TTL_SEL)
|
||||
{
|
||||
ui::usrSelPanel->openPanel();
|
||||
ui::ttlOptsPanel->openPanel();
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
|
||||
#include "gfx.h"
|
||||
#include "ui.h"
|
||||
#include "miscui.h"
|
||||
#include "util.h"
|
||||
#include "type.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
|
||||
static const SDL_Color divLight = {0x6D, 0x6D, 0x6D, 0xFF};
|
||||
static const SDL_Color divDark = {0xCC, 0xCC, 0xCC, 0xFF};
|
||||
static const SDL_Color shadow = {0x66, 0x66, 0x66, 0xFF};
|
||||
static const SDL_Color divLight = {0x6D, 0x6D, 0x6D, 0xFF};
|
||||
static const SDL_Color divDark = {0xCC, 0xCC, 0xCC, 0xFF};
|
||||
static const SDL_Color shadow = {0x66, 0x66, 0x66, 0xFF};
|
||||
|
||||
static const SDL_Color fillBack = {0x66, 0x66, 0x66, 0xFF};
|
||||
static const SDL_Color fillBar = {0x00, 0xDD, 0x00, 0xFF};
|
||||
static const SDL_Color fillBack = {0x66, 0x66, 0x66, 0xFF};
|
||||
static const SDL_Color fillBar = {0x00, 0xDD, 0x00, 0xFF};
|
||||
|
||||
static const SDL_Color menuColorLight = {0x32, 0x50, 0xF0, 0xFF};
|
||||
static const SDL_Color menuColorDark = {0x00, 0xFF, 0xC5, 0xFF};
|
||||
static const SDL_Color menuColorDark = {0x00, 0xFF, 0xC5, 0xFF};
|
||||
|
||||
ui::menu::menu(const int& _x, const int& _y, const int& _rW, const int& _fS, const int& _mL)
|
||||
ui::menu::menu(int _x, int _y, int _rW, int _fS, int _mL)
|
||||
{
|
||||
x = _x;
|
||||
mY = _y;
|
||||
|
|
@ -32,13 +32,12 @@ ui::menu::menu(const int& _x, const int& _y, const int& _rW, const int& _fS, con
|
|||
rH = _fS + 30;
|
||||
spcWidth = gfx::getTextWidth(" ", _fS) * 3;
|
||||
|
||||
optTex = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, rW - 24, rH - 8);
|
||||
SDL_SetTextureBlendMode(optTex, SDL_BLENDMODE_BLEND);
|
||||
optTex = gfx::texMgr->textureCreate(rW - 24, rH - 8);
|
||||
}
|
||||
|
||||
void ui::menu::editParam(int _param, int newVal)
|
||||
{
|
||||
switch(_param)
|
||||
switch (_param)
|
||||
{
|
||||
case MENU_X:
|
||||
x = newVal;
|
||||
|
|
@ -51,14 +50,22 @@ void ui::menu::editParam(int _param, int newVal)
|
|||
case MENU_RECT_WIDTH:
|
||||
rW = newVal;
|
||||
SDL_DestroyTexture(optTex);
|
||||
optTex = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, rW, rH);
|
||||
optTex = SDL_CreateTexture(gfx::render,
|
||||
SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
|
||||
rW,
|
||||
rH);
|
||||
SDL_SetTextureBlendMode(optTex, SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
|
||||
case MENU_RECT_HEIGHT:
|
||||
rH = newVal;
|
||||
SDL_DestroyTexture(optTex);
|
||||
optTex = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, rW, rH);
|
||||
optTex = SDL_CreateTexture(gfx::render,
|
||||
SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
|
||||
rW,
|
||||
rH);
|
||||
SDL_SetTextureBlendMode(optTex, SDL_BLENDMODE_BLEND);
|
||||
break;
|
||||
|
||||
|
|
@ -72,7 +79,7 @@ void ui::menu::editParam(int _param, int newVal)
|
|||
}
|
||||
}
|
||||
|
||||
int ui::menu::addOpt(SDL_Texture *_icn, const std::string& add)
|
||||
int ui::menu::addOpt(SDL_Texture *_icn, const std::string &add)
|
||||
{
|
||||
ui::menuOpt newOpt;
|
||||
newOpt.txt = add;
|
||||
|
|
@ -82,9 +89,9 @@ int ui::menu::addOpt(SDL_Texture *_icn, const std::string& add)
|
|||
return opt.size() - 1;
|
||||
}
|
||||
|
||||
void ui::menu::editOpt(int ind, SDL_Texture *_icn, const std::string& ch)
|
||||
void ui::menu::editOpt(int ind, SDL_Texture *_icn, const std::string &ch)
|
||||
{
|
||||
if(!opt[ind].icn && _icn)
|
||||
if (!opt[ind].icn && _icn)
|
||||
opt[ind].icn = _icn;
|
||||
|
||||
opt[ind].txt = ch;
|
||||
|
|
@ -97,11 +104,11 @@ void ui::menu::optAddButtonEvent(unsigned _ind, HidNpadButton _button, funcPtr _
|
|||
opt[_ind].events.push_back(newEvent);
|
||||
}
|
||||
|
||||
int ui::menu::getOptPos(const std::string& txt)
|
||||
int ui::menu::getOptPos(const std::string &txt)
|
||||
{
|
||||
for(unsigned i = 0; i < opt.size(); i++)
|
||||
for (unsigned i = 0; i < opt.size(); i++)
|
||||
{
|
||||
if(opt[i].txt == txt)
|
||||
if (opt[i].txt == txt)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
|
|
@ -110,55 +117,54 @@ int ui::menu::getOptPos(const std::string& txt)
|
|||
|
||||
ui::menu::~menu()
|
||||
{
|
||||
SDL_DestroyTexture(optTex);
|
||||
opt.clear();
|
||||
}
|
||||
|
||||
void ui::menu::update()
|
||||
{
|
||||
if(!isActive)
|
||||
if (!isActive)
|
||||
return;
|
||||
|
||||
uint64_t down = ui::padKeysDown();
|
||||
uint64_t held = ui::padKeysHeld();
|
||||
|
||||
if(held & HidNpadButton_AnyUp || held & HidNpadButton_AnyDown)
|
||||
if (held & HidNpadButton_AnyUp || held & HidNpadButton_AnyDown)
|
||||
++fc;
|
||||
else
|
||||
fc = 0;
|
||||
|
||||
if(fc > 10)
|
||||
if (fc > 10)
|
||||
fc = 0;
|
||||
|
||||
int oldSel = selected;
|
||||
int mSize = opt.size() - 1;
|
||||
bool change = false;
|
||||
if( (down & HidNpadButton_AnyUp) || ((held & HidNpadButton_AnyUp) && fc == 10) )
|
||||
if ((down & HidNpadButton_AnyUp) || ((held & HidNpadButton_AnyUp) && fc == 10))
|
||||
{
|
||||
if(--selected < 0)
|
||||
if (--selected < 0)
|
||||
selected = mSize;
|
||||
|
||||
change = true;
|
||||
}
|
||||
else if( (down & HidNpadButton_AnyDown) || ((held & HidNpadButton_AnyDown) && fc == 10))
|
||||
else if ((down & HidNpadButton_AnyDown) || ((held & HidNpadButton_AnyDown) && fc == 10))
|
||||
{
|
||||
if(++selected > mSize)
|
||||
if (++selected > mSize)
|
||||
selected = 0;
|
||||
|
||||
change = true;
|
||||
}
|
||||
else if(down & HidNpadButton_AnyLeft)
|
||||
else if (down & HidNpadButton_AnyLeft)
|
||||
{
|
||||
selected -= mL;
|
||||
if(selected < 0)
|
||||
if (selected < 0)
|
||||
selected = 0;
|
||||
|
||||
change = true;
|
||||
}
|
||||
else if(down & HidNpadButton_AnyRight)
|
||||
else if (down & HidNpadButton_AnyRight)
|
||||
{
|
||||
selected += mL;
|
||||
if(selected > mSize)
|
||||
if (selected > mSize)
|
||||
selected = mSize;
|
||||
|
||||
change = true;
|
||||
|
|
@ -166,120 +172,145 @@ void ui::menu::update()
|
|||
|
||||
++hoverCount;
|
||||
|
||||
if(!change && hoverCount >= 90)
|
||||
if (!change && hoverCount >= 90)
|
||||
hover = true;
|
||||
else if(change)
|
||||
else if (change)
|
||||
{
|
||||
hoverCount = 0;
|
||||
hover = false;
|
||||
}
|
||||
|
||||
if(down && !opt[selected].events.empty())
|
||||
if (down && !opt[selected].events.empty())
|
||||
{
|
||||
for(ui::menuOptEvent& e : opt[selected].events)
|
||||
for (ui::menuOptEvent &e : opt[selected].events)
|
||||
{
|
||||
if((down & e.button) && e.func)
|
||||
if ((down & e.button) && e.func)
|
||||
(*e.func)(e.args);
|
||||
}
|
||||
}
|
||||
|
||||
if(selected <= mL)
|
||||
if (selected <= mL)
|
||||
tY = mY;
|
||||
else if(selected >= (mSize - mL) && mSize > mL * 2)
|
||||
else if (selected >= (mSize - mL) && mSize > mL * 2)
|
||||
tY = mY + -(rH * (mSize - (mL * 2)));
|
||||
else if(selected > mL && selected < (mSize - mL))
|
||||
else if (selected > mL && selected < (mSize - mL))
|
||||
tY = -(rH * (selected - mL));
|
||||
|
||||
if(selected != oldSel && onChange)
|
||||
if (selected != oldSel && onChange)
|
||||
(*onChange)(NULL);
|
||||
|
||||
if(callback)
|
||||
if (callback)
|
||||
(*callback)(callbackArgs);
|
||||
}
|
||||
|
||||
void ui::menu::draw(SDL_Texture *target, const SDL_Color *textClr, bool drawText)
|
||||
{
|
||||
if(opt.size() < 1)
|
||||
if (opt.size() < 1)
|
||||
return;
|
||||
|
||||
int tH = 0;
|
||||
SDL_QueryTexture(target, NULL, NULL, NULL, &tH);
|
||||
|
||||
if(y != tY)
|
||||
if (y != tY)
|
||||
{
|
||||
float add = (float)((float)tY - (float)y) / ui::animScale;
|
||||
y += ceil(add);
|
||||
}
|
||||
|
||||
if(clrAdd)
|
||||
if (clrAdd)
|
||||
{
|
||||
clrSh += 6;
|
||||
if(clrSh >= 0x72)
|
||||
if (clrSh >= 0x72)
|
||||
clrAdd = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
clrSh -= 3;
|
||||
if(clrSh <= 0x00)
|
||||
if (clrSh <= 0x00)
|
||||
clrAdd = true;
|
||||
}
|
||||
|
||||
for(int i = 0, tY = y; i < (int)opt.size(); i++, tY += rH)
|
||||
for (int i = 0, tY = y; i < (int)opt.size(); i++, tY += rH)
|
||||
{
|
||||
if(tY < -rH || tY > tH)
|
||||
if (tY < -rH || tY > tH)
|
||||
continue;
|
||||
|
||||
gfx::clearTarget(optTex, &ui::transparent);
|
||||
|
||||
//Todo: Clean this up maybe
|
||||
if(i == selected && drawText)
|
||||
if (i == selected && drawText)
|
||||
{
|
||||
if(isActive)
|
||||
if (isActive)
|
||||
ui::drawBoundBox(target, x, y + (i * rH), rW, rH, clrSh);
|
||||
|
||||
gfx::drawRect(target, ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark, x + 10, ((y + (rH / 2 - fSize / 2)) + (i * rH)) - 2, 4, fSize + 4);
|
||||
gfx::drawRect(target,
|
||||
ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark,
|
||||
x + 10,
|
||||
((y + (rH / 2 - fSize / 2)) + (i * rH)) - 2,
|
||||
4,
|
||||
fSize + 4);
|
||||
|
||||
static int dX = 0;
|
||||
if(hover && opt[i].txtWidth > rW - 24)
|
||||
if (hover && opt[i].txtWidth > rW - 24)
|
||||
{
|
||||
if((dX -= 2) <= -(opt[i].txtWidth + spcWidth))
|
||||
if ((dX -= 2) <= -(opt[i].txtWidth + spcWidth))
|
||||
{
|
||||
dX = 0;
|
||||
hoverCount = 0;
|
||||
hover = false;
|
||||
}
|
||||
|
||||
gfx::drawTextf(optTex, fSize, dX, ((rH - 8) / 2) - fSize / 2, ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark, opt[i].txt.c_str());
|
||||
gfx::drawTextf(optTex, fSize, dX + opt[i].txtWidth + spcWidth, ((rH - 8) / 2) - fSize / 2, ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark, opt[i].txt.c_str());
|
||||
gfx::drawTextf(optTex,
|
||||
fSize,
|
||||
dX,
|
||||
((rH - 8) / 2) - fSize / 2,
|
||||
ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark,
|
||||
opt[i].txt.c_str());
|
||||
gfx::drawTextf(optTex,
|
||||
fSize,
|
||||
dX + opt[i].txtWidth + spcWidth,
|
||||
((rH - 8) / 2) - fSize / 2,
|
||||
ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark,
|
||||
opt[i].txt.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
dX = 0;
|
||||
gfx::drawTextf(optTex, fSize, 0, ((rH - 8) / 2) - fSize / 2, ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark, opt[i].txt.c_str());
|
||||
gfx::drawTextf(optTex,
|
||||
fSize,
|
||||
0,
|
||||
((rH - 8) / 2) - fSize / 2,
|
||||
ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark,
|
||||
opt[i].txt.c_str());
|
||||
}
|
||||
gfx::texDraw(target, optTex, x + 20, (y + 4 + (i * rH)));
|
||||
}
|
||||
else if(i == selected && !drawText && opt[i].icn)
|
||||
else if (i == selected && !drawText && opt[i].icn)
|
||||
{
|
||||
int w, h;
|
||||
SDL_QueryTexture(opt[i].icn, NULL, NULL, &w, &h);
|
||||
float scale = (float)fSize / (float)h;
|
||||
int dW = scale * w;
|
||||
int dH = scale * h;
|
||||
if(isActive)
|
||||
if (isActive)
|
||||
ui::drawBoundBox(target, x, y + (i * rH), rW, rH, clrSh);
|
||||
|
||||
gfx::drawRect(target, ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark, x + 10, ((y + (rH / 2 - fSize / 2)) + (i * rH)) - 2, 4, fSize + 4);
|
||||
gfx::drawRect(target,
|
||||
ui::thmID == ColorSetId_Light ? &menuColorLight : &menuColorDark,
|
||||
x + 10,
|
||||
((y + (rH / 2 - fSize / 2)) + (i * rH)) - 2,
|
||||
4,
|
||||
fSize + 4);
|
||||
gfx::texDrawStretch(optTex, opt[i].icn, 0, ((rH - 8) / 2) - fSize / 2, dW, dH);
|
||||
gfx::texDraw(target, optTex, x + 20, (y + 4 + (i * rH)));
|
||||
}
|
||||
else if(drawText)
|
||||
else if (drawText)
|
||||
{
|
||||
gfx::clearTarget(optTex, &ui::transparent);
|
||||
gfx::drawTextf(optTex, fSize, 0, ((rH - 8) / 2) - fSize / 2, textClr, opt[i].txt.c_str());
|
||||
gfx::texDraw(target, optTex, x + 20, (y + 4 + (i * rH)));
|
||||
}
|
||||
else if(!drawText && opt[i].icn)
|
||||
else if (!drawText && opt[i].icn)
|
||||
{
|
||||
int w, h;
|
||||
SDL_QueryTexture(opt[i].icn, NULL, NULL, &w, &h);
|
||||
|
|
@ -306,15 +337,15 @@ void ui::menu::setActive(bool _set)
|
|||
isActive = _set;
|
||||
}
|
||||
|
||||
void ui::progBar::update(const uint64_t& _prog)
|
||||
void ui::progBar::update(uint64_t _prog)
|
||||
{
|
||||
prog = _prog;
|
||||
|
||||
if(max > 0)
|
||||
if (max > 0)
|
||||
width = (float)((float)prog / (float)max) * 648.0f;
|
||||
}
|
||||
|
||||
void ui::progBar::draw(const std::string& text)
|
||||
void ui::progBar::draw(const std::string &text)
|
||||
{
|
||||
char progStr[64];
|
||||
sprintf(progStr, "%.2fMB / %.2fMB", (float)prog / 1024.0f / 1024.0f, (float)max / 1024.0f / 1024.0f);
|
||||
|
|
@ -336,29 +367,29 @@ ui::popMessageMngr::~popMessageMngr()
|
|||
void ui::popMessageMngr::update()
|
||||
{
|
||||
//Anything that needs graphics/text must be processed on main thread
|
||||
for(ui::popMessage& p : popQueue)
|
||||
for (ui::popMessage &p : popQueue)
|
||||
{
|
||||
p.rectWidth = gfx::getTextWidth(p.message.c_str(), 24) + 32;
|
||||
message.push_back(p);
|
||||
}
|
||||
popQueue.clear();
|
||||
|
||||
for(unsigned i = 0; i < message.size(); i++)
|
||||
for (unsigned i = 0; i < message.size(); i++)
|
||||
{
|
||||
message[i].frames--;
|
||||
if(message[i].frames <= 0)
|
||||
if (message[i].frames <= 0)
|
||||
message.erase(message.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
void ui::popMessageMngr::popMessageAdd(const std::string& mess, int frameTime)
|
||||
void ui::popMessageMngr::popMessageAdd(const std::string &mess, int frameTime)
|
||||
{
|
||||
//Suppress multiple of the same
|
||||
int lastMessageIndex = message.size() - 1;
|
||||
std::string lastMessage;
|
||||
if(lastMessageIndex >= 0)
|
||||
if (lastMessageIndex >= 0)
|
||||
lastMessage = message[lastMessageIndex].message;
|
||||
if(lastMessage != mess)
|
||||
if (lastMessage != mess)
|
||||
{
|
||||
ui::popMessage newPop = {mess, 0, frameTime};
|
||||
popQueue.push_back(newPop);
|
||||
|
|
@ -368,10 +399,10 @@ void ui::popMessageMngr::popMessageAdd(const std::string& mess, int frameTime)
|
|||
void ui::popMessageMngr::draw()
|
||||
{
|
||||
int y = 640;
|
||||
for(auto& p : message)
|
||||
for (auto &p : message)
|
||||
{
|
||||
y -= 48;
|
||||
if(p.y != y)
|
||||
if (p.y != y)
|
||||
p.y += (y - p.y) / ui::animScale;
|
||||
|
||||
gfx::drawRect(NULL, &ui::tboxClr, 64, p.y, p.rectWidth, 40);
|
||||
|
|
@ -379,7 +410,12 @@ void ui::popMessageMngr::draw()
|
|||
}
|
||||
}
|
||||
|
||||
ui::confirmArgs *ui::confirmArgsCreate(bool _hold, funcPtr _confFunc, funcPtr _cancelFunc, void *_funcArgs, const char *fmt, ...)
|
||||
ui::confirmArgs *ui::confirmArgsCreate(bool _hold,
|
||||
funcPtr _confFunc,
|
||||
funcPtr _cancelFunc,
|
||||
void *_funcArgs,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
char tmp[1024];
|
||||
va_list args;
|
||||
|
|
@ -403,35 +439,35 @@ void confirm_t(void *a)
|
|||
threadInfo *t = (threadInfo *)a;
|
||||
ui::confirmArgs *c = (ui::confirmArgs *)t->argPtr;
|
||||
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
uint64_t down = ui::padKeysDown();
|
||||
uint64_t held = ui::padKeysHeld();
|
||||
|
||||
if((down & HidNpadButton_A) && !c->hold)
|
||||
if ((down & HidNpadButton_A) && !c->hold)
|
||||
{
|
||||
ui::newThread(c->confFunc, c->args, NULL);
|
||||
break;
|
||||
}
|
||||
else if((held & HidNpadButton_A) && c->hold)
|
||||
else if ((held & HidNpadButton_A) && c->hold)
|
||||
{
|
||||
if(c->frameCount % 4 == 0 && ++c->lgFrame > 7)
|
||||
if (c->frameCount % 4 == 0 && ++c->lgFrame > 7)
|
||||
c->lgFrame = 0;
|
||||
|
||||
if(c->frameCount >= 120)
|
||||
if (c->frameCount >= 120)
|
||||
{
|
||||
ui::newThread(c->confFunc, c->args, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(down & HidNpadButton_B)
|
||||
else if (down & HidNpadButton_B)
|
||||
{
|
||||
if(c->cancelFunc)
|
||||
if (c->cancelFunc)
|
||||
(*c->cancelFunc)(c->args);
|
||||
break;
|
||||
}
|
||||
|
||||
svcSleepThread(1e+9 / 60);//Close enough
|
||||
svcSleepThread(1e+9 / 60); //Close enough
|
||||
}
|
||||
delete c;
|
||||
t->finished = true;
|
||||
|
|
@ -442,19 +478,19 @@ void confirmDrawFunc(void *a)
|
|||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
ui::confirmArgs *c = (ui::confirmArgs *)t->argPtr;
|
||||
if(!t->finished)
|
||||
if (!t->finished)
|
||||
{
|
||||
std::string yesTxt = ui::getUIString("dialogYes", 0);
|
||||
unsigned yesX = 0;
|
||||
|
||||
if((ui::padKeysHeld() & HidNpadButton_A) && c->hold)
|
||||
if ((ui::padKeysHeld() & HidNpadButton_A) && c->hold)
|
||||
{
|
||||
c->frameCount++;
|
||||
if(c->frameCount <= 40)
|
||||
if (c->frameCount <= 40)
|
||||
yesTxt = ui::getUIString("holdingText", 0);
|
||||
else if(c->frameCount <= 80)
|
||||
else if (c->frameCount <= 80)
|
||||
yesTxt = ui::getUIString("holdingText", 1);
|
||||
else if(c->frameCount <= 120)
|
||||
else if (c->frameCount <= 120)
|
||||
yesTxt = ui::getUICString("holdingText", 2);
|
||||
|
||||
yesTxt += ui::loadGlyphArray[c->lgFrame];
|
||||
|
|
@ -483,9 +519,9 @@ static void showMessage_t(void *a)
|
|||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
std::string *text = (std::string *)t->argPtr;
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
if(ui::padKeysDown() & HidNpadButton_A)
|
||||
if (ui::padKeysDown() & HidNpadButton_A)
|
||||
break;
|
||||
|
||||
svcSleepThread(1e+9 / 60);
|
||||
|
|
@ -498,7 +534,7 @@ static void showMessageDrawFunc(void *a)
|
|||
{
|
||||
threadInfo *t = (threadInfo *)a;
|
||||
std::string *text = (std::string *)t->argPtr;
|
||||
if(!t->finished)
|
||||
if (!t->finished)
|
||||
{
|
||||
unsigned okX = 640 - (gfx::getTextWidth(ui::getUICString("dialogOK", 0), 18) / 2);
|
||||
ui::drawTextbox(NULL, 280, 262, 720, 256);
|
||||
|
|
@ -528,7 +564,7 @@ void ui::drawTextbox(SDL_Texture *target, int x, int y, int w, int h)
|
|||
gfx::texDraw(target, ui::cornerTopRight, (x + w) - 32, y);
|
||||
|
||||
//middle
|
||||
gfx::drawRect(target, &ui::tboxClr, x, y + 32, w, h - 64);
|
||||
gfx::drawRect(target, &ui::tboxClr, x, y + 32, w, h - 64);
|
||||
|
||||
//bottom
|
||||
gfx::texDraw(target, ui::cornerBottomLeft, x, (y + h) - 32);
|
||||
|
|
@ -541,7 +577,7 @@ void ui::drawBoundBox(SDL_Texture *target, int x, int y, int w, int h, uint8_t c
|
|||
SDL_SetRenderTarget(gfx::render, target);
|
||||
SDL_Color rectClr;
|
||||
|
||||
if(ui::thmID == ColorSetId_Light)
|
||||
if (ui::thmID == ColorSetId_Light)
|
||||
rectClr = {0xFD, 0xFD, 0xFD, 0xFF};
|
||||
else
|
||||
rectClr = {0x21, 0x22, 0x21, 0xFF};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include <switch.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "file.h"
|
||||
|
|
@ -228,6 +228,7 @@ static void toggleOpt(void *a)
|
|||
ui::animScale = 1;
|
||||
break;
|
||||
}
|
||||
cfg::saveConfig();
|
||||
}
|
||||
|
||||
static void updateMenuText()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include <SDL.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "ui.h"
|
||||
#include "gfx.h"
|
||||
|
|
@ -15,13 +15,7 @@ ui::slideOutPanel::slideOutPanel(int _w, int _h, int _y, ui::slidePanelOrientati
|
|||
x = 1280;
|
||||
|
||||
drawFunc = _draw;
|
||||
panel = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
SDL_SetTextureBlendMode(panel, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
ui::slideOutPanel::~slideOutPanel()
|
||||
{
|
||||
SDL_DestroyTexture(panel);
|
||||
panel = gfx::texMgr->textureCreate(w, h);
|
||||
}
|
||||
|
||||
void ui::slideOutPanel::resizePanel(int _w, int _h, int _y)
|
||||
|
|
@ -32,9 +26,7 @@ void ui::slideOutPanel::resizePanel(int _w, int _h, int _y)
|
|||
if(sldSide == ui::SLD_LEFT)
|
||||
x = -w;
|
||||
|
||||
SDL_DestroyTexture(panel);
|
||||
panel = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
SDL_SetTextureBlendMode(panel, SDL_BLENDMODE_BLEND);
|
||||
gfx::texMgr->textureResize(&panel, w, h);
|
||||
}
|
||||
|
||||
void ui::slideOutPanel::update()
|
||||
|
|
|
|||
210
src/ui/ttl.cpp
210
src/ui/ttl.cpp
|
|
@ -1,10 +1,10 @@
|
|||
#include <vector>
|
||||
|
||||
#include "ui.h"
|
||||
#include "ttl.h"
|
||||
#include "file.h"
|
||||
#include "util.h"
|
||||
#include "cfg.h"
|
||||
#include "file.h"
|
||||
#include "ttl.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
|
||||
static int ttlHelpX = 0;
|
||||
static std::vector<ui::titleview *> ttlViews;
|
||||
|
|
@ -16,7 +16,7 @@ static Mutex ttlViewLock = 0;
|
|||
void ui::ttlRefresh()
|
||||
{
|
||||
mutexLock(&ttlViewLock);
|
||||
for(int i = 0; i < (int)data::users.size(); i++)
|
||||
for (int i = 0; i < (int)data::users.size(); i++)
|
||||
ttlViews[i]->refresh();
|
||||
mutexUnlock(&ttlViewLock);
|
||||
}
|
||||
|
|
@ -29,12 +29,12 @@ static void ttlViewCallback(void *a)
|
|||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
uint64_t tid = d->tid;
|
||||
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_A:
|
||||
if(fs::mountSave(d->saveInfo))
|
||||
if (fs::mountSave(d->saveInfo))
|
||||
{
|
||||
ttlViews[curUserIndex]->setActive(false, true);
|
||||
ttlViews[curUserIndex]->setActive(false, true);
|
||||
ui::fldPopulateMenu();
|
||||
}
|
||||
break;
|
||||
|
|
@ -52,28 +52,28 @@ static void ttlViewCallback(void *a)
|
|||
break;
|
||||
|
||||
case HidNpadButton_Y:
|
||||
{
|
||||
cfg::addTitleToFavorites(tid);
|
||||
int newSel = data::getTitleIndexInUser(data::users[curUserIndex], tid);
|
||||
ttlViews[curUserIndex]->setSelected(newSel);
|
||||
}
|
||||
break;
|
||||
{
|
||||
cfg::addTitleToFavorites(tid);
|
||||
int newSel = data::getTitleIndexInUser(data::users[curUserIndex], tid);
|
||||
ttlViews[curUserIndex]->setSelected(newSel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ttlOptsCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
{
|
||||
int curUserIndex = data::getCurrentUserIndex();
|
||||
ttlOpts->setActive(false);
|
||||
ui::ttlOptsPanel->closePanel();
|
||||
ttlViews[curUserIndex]->setActive(true, true);
|
||||
ui::updateInput();
|
||||
}
|
||||
break;
|
||||
{
|
||||
int curUserIndex = data::getCurrentUserIndex();
|
||||
ttlOpts->setActive(false);
|
||||
ui::ttlOptsPanel->closePanel();
|
||||
ttlViews[curUserIndex]->setActive(true, true);
|
||||
ui::updateInput();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,14 +86,14 @@ static void ttlOptsPanelDraw(void *a)
|
|||
static void ttlOptsShowInfoPanel(void *)
|
||||
{
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(utinfo->tid);
|
||||
|
||||
char tmp[256];
|
||||
sprintf(tmp, ui::getUICString("infoStatus", 4), tinfo->author.c_str());
|
||||
|
||||
size_t titleWidth = gfx::getTextWidth(tinfo->title.c_str(), 18);
|
||||
size_t pubWidth = gfx::getTextWidth(tmp, 18);
|
||||
if(titleWidth > 410 || pubWidth > 410)
|
||||
if (titleWidth > 410 || pubWidth > 410)
|
||||
{
|
||||
size_t newWidth = titleWidth > pubWidth ? titleWidth : pubWidth;
|
||||
infoPanel->resizePanel(newWidth + 40, 720, 0);
|
||||
|
|
@ -109,7 +109,12 @@ static void ttlOptsShowInfoPanel(void *)
|
|||
static void ttlOptsBlacklistTitle(void *a)
|
||||
{
|
||||
std::string title = data::getTitleNameByTID(data::getCurrentUserTitleInfo()->tid);
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(false, cfg::addTitleToBlacklist, NULL, NULL, ui::getUICString("confirmBlacklist", 0), title.c_str());
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(false,
|
||||
cfg::addTitleToBlacklist,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("confirmBlacklist", 0),
|
||||
title.c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
|
||||
|
|
@ -117,15 +122,16 @@ static void ttlOptsDefinePath(void *a)
|
|||
{
|
||||
uint64_t tid = data::getCurrentUserTitleInfo()->tid;
|
||||
std::string safeTitle = data::getTitleInfoByTID(tid)->safeTitle;
|
||||
std::string newSafeTitle = util::getStringInput(SwkbdType_QWERTY, safeTitle, ui::getUICString("swkbdNewSafeTitle", 0), 0x200, 0, NULL);
|
||||
if(!newSafeTitle.empty())
|
||||
std::string newSafeTitle =
|
||||
util::getStringInput(SwkbdType_QWERTY, safeTitle, ui::getUICString("swkbdNewSafeTitle", 0), 0x200, 0, NULL);
|
||||
if (!newSafeTitle.empty())
|
||||
cfg::pathDefAdd(tid, newSafeTitle);
|
||||
}
|
||||
|
||||
static void ttlOptsToFileMode(void *a)
|
||||
{
|
||||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
if(fs::mountSave(d->saveInfo))
|
||||
if (fs::mountSave(d->saveInfo))
|
||||
{
|
||||
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||
std::string sdmcPath = util::generatePathByTID(utinfo->tid);
|
||||
|
|
@ -143,10 +149,10 @@ static void ttlOptsDeleteAllBackups_t(void *a)
|
|||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
std::string targetPath = util::generatePathByTID(d->tid);
|
||||
fs::dirList *backupList = new fs::dirList(targetPath);
|
||||
for(unsigned i = 0; i < backupList->getCount(); i++)
|
||||
for (unsigned i = 0; i < backupList->getCount(); i++)
|
||||
{
|
||||
std::string delPath = targetPath + backupList->getItem(i);
|
||||
if(backupList->isDir(i))
|
||||
if (backupList->isDir(i))
|
||||
{
|
||||
delPath += "/";
|
||||
fs::delDir(delPath);
|
||||
|
|
@ -163,7 +169,12 @@ static void ttlOptsDeleteAllBackups(void *a)
|
|||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
std::string currentTitle = data::getTitleNameByTID(d->tid);
|
||||
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(cfg::config["holdDel"], ttlOptsDeleteAllBackups_t, NULL, NULL, ui::getUICString("confirmDeleteBackupsTitle", 0), currentTitle.c_str());
|
||||
ui::confirmArgs *send = ui::confirmArgsCreate(cfg::config["holdDel"],
|
||||
ttlOptsDeleteAllBackups_t,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("confirmDeleteBackupsTitle", 0),
|
||||
currentTitle.c_str());
|
||||
ui::confirm(send);
|
||||
}
|
||||
|
||||
|
|
@ -185,10 +196,15 @@ static void ttlOptsResetSaveData_t(void *a)
|
|||
static void ttlOptsResetSaveData(void *a)
|
||||
{
|
||||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
if(d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
if (d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
{
|
||||
std::string title = data::getTitleNameByTID(d->tid);
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], ttlOptsResetSaveData_t, NULL, NULL, ui::getUICString("confirmResetSaveData", 0), title.c_str());
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"],
|
||||
ttlOptsResetSaveData_t,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("confirmResetSaveData", 0),
|
||||
title.c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
}
|
||||
|
|
@ -202,13 +218,14 @@ static void ttlOptsDeleteSaveData_t(void *a)
|
|||
|
||||
std::string title = data::getTitleNameByTID(d->tid);
|
||||
t->status->setStatus(ui::getUICString("threadStatusDeletingSaveData", 0), title.c_str());
|
||||
if(R_SUCCEEDED(fsDeleteSaveDataFileSystemBySaveDataSpaceId((FsSaveDataSpaceId)d->saveInfo.save_data_space_id, d->saveInfo.save_data_id)))
|
||||
if (R_SUCCEEDED(fsDeleteSaveDataFileSystemBySaveDataSpaceId((FsSaveDataSpaceId)d->saveInfo.save_data_space_id,
|
||||
d->saveInfo.save_data_id)))
|
||||
{
|
||||
data::loadUsersTitles(false);
|
||||
if(u->titleInfo.size() == 0)
|
||||
if (u->titleInfo.size() == 0)
|
||||
{
|
||||
//Kick back to user
|
||||
ui::ttlOptsPanel->closePanel();//JIC
|
||||
ui::ttlOptsPanel->closePanel(); //JIC
|
||||
ttlOpts->setActive(false);
|
||||
ttlViews[userIndex]->setActive(false, false);
|
||||
ui::usrMenu->setActive(true);
|
||||
|
|
@ -223,10 +240,15 @@ static void ttlOptsDeleteSaveData_t(void *a)
|
|||
static void ttlOptsDeleteSaveData(void *a)
|
||||
{
|
||||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
if(d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
if (d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
{
|
||||
std::string title = data::getTitleNameByTID(d->tid);
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"], ttlOptsDeleteSaveData_t, NULL, NULL, ui::getUICString("confirmDeleteSaveData", 0), title.c_str());
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(cfg::config["holdDel"],
|
||||
ttlOptsDeleteSaveData_t,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("confirmDeleteSaveData", 0),
|
||||
title.c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
}
|
||||
|
|
@ -234,9 +256,10 @@ static void ttlOptsDeleteSaveData(void *a)
|
|||
static void ttlOptsExtendSaveData(void *a)
|
||||
{
|
||||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
if(d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
if (d->saveInfo.save_data_type != FsSaveDataType_System)
|
||||
{
|
||||
std::string expSizeStr = util::getStringInput(SwkbdType_NumPad, "", ui::getUICString("swkbdExpandSize", 0), 4, 0, NULL);
|
||||
std::string expSizeStr =
|
||||
util::getStringInput(SwkbdType_NumPad, "", ui::getUICString("swkbdExpandSize", 0), 4, 0, NULL);
|
||||
uint64_t extMB = strtoul(expSizeStr.c_str(), NULL, 10) * 0x100000;
|
||||
fs::extendSaveDataThreaded(d, extMB);
|
||||
}
|
||||
|
|
@ -244,27 +267,36 @@ static void ttlOptsExtendSaveData(void *a)
|
|||
|
||||
static void ttlOptsExportSVI(void *a)
|
||||
{
|
||||
data::userTitleInfo *ut = data::getCurrentUserTitleInfo();
|
||||
data::titleInfo *t = data::getTitleInfoByTID(ut->tid);
|
||||
std::string out = fs::getWorkDir() + "svi/";
|
||||
fs::mkDir(out.substr(0, out.length() - 1));
|
||||
out += util::getIDStr(ut->tid) + ".svi";
|
||||
FILE *svi = fopen(out.c_str(), "wb");
|
||||
if(svi)
|
||||
{
|
||||
//Grab icon
|
||||
NsApplicationControlData *ctrlData = new NsApplicationControlData;
|
||||
uint64_t ctrlSize = 0;
|
||||
nsGetApplicationControlData(NsApplicationControlSource_Storage, ut->tid, ctrlData, sizeof(NsApplicationControlData), &ctrlSize);
|
||||
size_t jpegSize = ctrlSize - sizeof(ctrlData->nacp);
|
||||
// Grab the current user title info.
|
||||
data::userTitleInfo *userInfo = data::getCurrentUserTitleInfo();
|
||||
|
||||
fwrite(&ut->tid, sizeof(uint64_t), 1, svi);
|
||||
fwrite(&t->nacp, sizeof(NacpStruct), 1, svi);
|
||||
fwrite(ctrlData->icon, 1, jpegSize, svi);
|
||||
fclose(svi);
|
||||
delete ctrlData;
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSVIExported", 0));
|
||||
// Use that to grab the corresponding title info.
|
||||
data::titleInfo *info = data::getTitleInfoByTID(userInfo->tid);
|
||||
|
||||
// Ensure the svi directory exists.
|
||||
std::string sviPath = fs::getWorkDir() + "svi/";
|
||||
fs::mkDir(sviPath.substr(0, sviPath.length() - 1));
|
||||
|
||||
// Append the application ID and extension.
|
||||
sviPath += util::getIDStr(userInfo->tid) + ".svi";
|
||||
|
||||
// Try to open the file for writing.
|
||||
std::FILE *sviFile = std::fopen(sviPath.data(), "wb");
|
||||
if (!sviFile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Write the application ID
|
||||
std::fwrite(&userInfo->tid, 1, sizeof(uint64_t), sviFile);
|
||||
// Control data.
|
||||
std::fwrite(&info->data, 1, sizeof(NacpStruct), sviFile);
|
||||
|
||||
// Close the file.
|
||||
std::fclose(sviFile);
|
||||
|
||||
// Toast.
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSVIExported", 0));
|
||||
}
|
||||
|
||||
static void infoPanelDraw(void *a)
|
||||
|
|
@ -292,39 +324,69 @@ static void infoPanelDraw(void *a)
|
|||
drawY += 40;
|
||||
|
||||
gfx::drawRect(panel, &ui::rectSh, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 1), d->saveInfo.save_data_id);
|
||||
gfx::drawTextf(panel,
|
||||
18,
|
||||
20,
|
||||
drawY + 10,
|
||||
&ui::txtCont,
|
||||
ui::getUICString("infoStatus", 1),
|
||||
d->saveInfo.save_data_id);
|
||||
drawY += 40;
|
||||
|
||||
uint32_t hours, mins;
|
||||
hours = d->playStats.playtimeMinutes / 60;
|
||||
mins = d->playStats.playtimeMinutes - (hours * 60);
|
||||
hours = ((d->playStats.playtime / 1e+9) / 60) / 60;
|
||||
mins = ((d->playStats.playtime / 1e+9) / 60) - (hours * 60);
|
||||
gfx::drawRect(panel, &ui::rectLt, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 2), hours, mins);
|
||||
drawY += 40;
|
||||
|
||||
gfx::drawRect(panel, &ui::rectSh, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 3), d->playStats.totalLaunches);
|
||||
gfx::drawTextf(panel,
|
||||
18,
|
||||
20,
|
||||
drawY + 10,
|
||||
&ui::txtCont,
|
||||
ui::getUICString("infoStatus", 3),
|
||||
d->playStats.total_launches);
|
||||
drawY += 40;
|
||||
|
||||
gfx::drawRect(panel, &ui::rectLt, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 5), ui::getUICString("saveDataTypeText", d->saveInfo.save_data_type));
|
||||
gfx::drawTextf(panel,
|
||||
18,
|
||||
20,
|
||||
drawY + 10,
|
||||
&ui::txtCont,
|
||||
ui::getUICString("infoStatus", 5),
|
||||
ui::getUICString("saveDataTypeText", d->saveInfo.save_data_type));
|
||||
drawY += 40;
|
||||
|
||||
uint8_t saveType = d->saveInfo.save_data_type;
|
||||
if(saveType == FsSaveDataType_Cache)
|
||||
if (saveType == FsSaveDataType_Cache)
|
||||
{
|
||||
gfx::drawRect(panel, &ui::rectSh, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 6), d->saveInfo.save_data_index);
|
||||
gfx::drawTextf(panel,
|
||||
18,
|
||||
20,
|
||||
drawY + 10,
|
||||
&ui::txtCont,
|
||||
ui::getUICString("infoStatus", 6),
|
||||
d->saveInfo.save_data_index);
|
||||
drawY += 40;
|
||||
}
|
||||
|
||||
gfx::drawRect(panel, saveType == FsSaveDataType_Cache ? &ui::rectLt : &ui::rectSh, 10, drawY, rectWidth, 38);
|
||||
gfx::drawTextf(panel, 18, 20, drawY + 10, &ui::txtCont, ui::getUICString("infoStatus", 7), data::getCurrentUser()->getUsername().c_str());
|
||||
gfx::drawTextf(panel,
|
||||
18,
|
||||
20,
|
||||
drawY + 10,
|
||||
&ui::txtCont,
|
||||
ui::getUICString("infoStatus", 7),
|
||||
data::getCurrentUser()->getUsername().c_str());
|
||||
}
|
||||
|
||||
static void infoPanelCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
infoPanel->closePanel();
|
||||
|
|
@ -339,7 +401,7 @@ void ui::ttlInit()
|
|||
{
|
||||
ttlHelpX = 1220 - gfx::getTextWidth(ui::getUICString("helpTitle", 0), 18);
|
||||
|
||||
for(data::user& u : data::users)
|
||||
for (data::user &u : data::users)
|
||||
ttlViews.emplace_back(new ui::titleview(u, 128, 128, 16, 16, 7, ttlViewCallback));
|
||||
|
||||
ttlOpts = new ui::menu(10, 32, 390, 20, 7);
|
||||
|
|
@ -354,7 +416,7 @@ void ui::ttlInit()
|
|||
infoPanel->setCallback(infoPanelCallback, NULL);
|
||||
|
||||
ttlOpts->setActive(false);
|
||||
for(int i = 0; i < 9; i++)
|
||||
for (int i = 0; i < 9; i++)
|
||||
ttlOpts->addOpt(NULL, ui::getUIString("titleOptions", i));
|
||||
|
||||
//Information
|
||||
|
|
@ -379,9 +441,9 @@ void ui::ttlInit()
|
|||
|
||||
void ui::ttlExit()
|
||||
{
|
||||
for(ui::titleview *t : ttlViews)
|
||||
for (ui::titleview *t : ttlViews)
|
||||
delete t;
|
||||
|
||||
|
||||
delete ttlOptsPanel;
|
||||
delete ttlOpts;
|
||||
delete infoPanel;
|
||||
|
|
@ -409,6 +471,6 @@ void ui::ttlDraw(SDL_Texture *target)
|
|||
mutexLock(&ttlViewLock);
|
||||
ttlViews[curUserIndex]->draw(target);
|
||||
mutexUnlock(&ttlViewLock);
|
||||
if(ui::mstate == TTL_SEL && !fldPanel->isOpen())
|
||||
if (ui::mstate == TTL_SEL && !fldPanel->isOpen())
|
||||
gfx::drawTextf(NULL, 18, ttlHelpX, 673, &ui::txtCont, ui::getUICString("helpTitle", 0));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ void ui::initStrings()
|
|||
addUIString("settingsMenu", 18, "Enable Trash Bin: ");
|
||||
addUIString("settingsMenu", 19, "Title Sorting Type: ");
|
||||
addUIString("settingsMenu", 20, "Animation Scale: ");
|
||||
addUIString("settingsMenu", 21, "Auto-upload to Drive/Webdav: ");
|
||||
|
||||
//Main menu
|
||||
addUIString("mainMenuSettings", 0, "Settings");
|
||||
|
|
@ -286,7 +287,9 @@ void ui::initStrings()
|
|||
addUIString("popSVIExported", 0, "SVI Exported.");
|
||||
addUIString("popDriveStarted", 0, "Google Drive started successfully.");
|
||||
addUIString("popDriveFailed", 0, "Failed to start Google Drive.");
|
||||
addUIString("popDriveNotActive", 0, "Google Drive is not available");
|
||||
addUIString("popRemoteNotActive", 0, "Remote is not available");
|
||||
addUIString("popWebdavStarted", 0, "Webdav started successfully.");
|
||||
addUIString("popWebdavFailed", 0, "Failed to start Webdav.");
|
||||
|
||||
//Keyboard hints
|
||||
addUIString("swkbdEnterName", 0, "Enter a new name");
|
||||
|
|
@ -314,6 +317,8 @@ void ui::initStrings()
|
|||
addUIString("debugStatus", 2, "Current Title: ");
|
||||
addUIString("debugStatus", 3, "Safe Title: ");
|
||||
addUIString("debugStatus", 4, "Sort Type: ");
|
||||
|
||||
addUIString("appletModeWarning", 0, "*WARNING*: You are running JKSV in applet mode. Certain functions may not work.");
|
||||
}
|
||||
|
||||
void ui::loadTrans()
|
||||
|
|
@ -335,6 +340,7 @@ void ui::loadTrans()
|
|||
util::replaceButtonsInString(ui::strings[std::make_pair("dialogYes", 0)]);
|
||||
util::replaceButtonsInString(ui::strings[std::make_pair("dialogNo", 0)]);
|
||||
util::replaceButtonsInString(ui::strings[std::make_pair("dialogOK", 0)]);
|
||||
util::replaceButtonsInString(ui::strings[std::make_pair("appletModeWarning", 0)]);
|
||||
}
|
||||
|
||||
void ui::saveTranslationFiles(void *a)
|
||||
|
|
|
|||
148
src/ui/usr.cpp
148
src/ui/usr.cpp
|
|
@ -1,16 +1,16 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <switch.h>
|
||||
#include <vector>
|
||||
|
||||
#include "file.h"
|
||||
#include "data.h"
|
||||
#include "file.h"
|
||||
#include "type.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
#include "type.h"
|
||||
|
||||
#include "usr.h"
|
||||
#include "ttl.h"
|
||||
#include "usr.h"
|
||||
|
||||
static const char *settText = ui::getUICString("mainMenuSettings", 0), *extText = ui::getUICString("mainMenuExtras", 0);
|
||||
|
||||
|
|
@ -34,30 +34,30 @@ static unsigned usrHelpX = 0;
|
|||
//Sort save create tids alphabetically by title from data
|
||||
static struct
|
||||
{
|
||||
bool operator()(const uint64_t& tid1, const uint64_t& tid2)
|
||||
{
|
||||
std::string tid1Title = data::getTitleNameByTID(tid1);
|
||||
std::string tid2Title = data::getTitleNameByTID(tid2);
|
||||
|
||||
uint32_t pointA = 0, pointB = 0;
|
||||
for(unsigned i = 0, j = 0; i < tid1Title.length(); )
|
||||
bool operator()(const uint64_t &tid1, const uint64_t &tid2)
|
||||
{
|
||||
ssize_t tid1Cnt = decode_utf8(&pointA, (const uint8_t *)&tid1Title.c_str()[i]);
|
||||
ssize_t tid2Cnt = decode_utf8(&pointB, (const uint8_t *)&tid2Title.c_str()[j]);
|
||||
std::string tid1Title = data::getTitleNameByTID(tid1);
|
||||
std::string tid2Title = data::getTitleNameByTID(tid2);
|
||||
|
||||
pointA = tolower(pointA), pointB = tolower(pointB);
|
||||
if(pointA != pointB)
|
||||
return pointA < pointB;
|
||||
uint32_t pointA = 0, pointB = 0;
|
||||
for (unsigned i = 0, j = 0; i < tid1Title.length();)
|
||||
{
|
||||
ssize_t tid1Cnt = decode_utf8(&pointA, (const uint8_t *)&tid1Title.c_str()[i]);
|
||||
ssize_t tid2Cnt = decode_utf8(&pointB, (const uint8_t *)&tid2Title.c_str()[j]);
|
||||
|
||||
i += tid1Cnt, j += tid2Cnt;
|
||||
pointA = tolower(pointA), pointB = tolower(pointB);
|
||||
if (pointA != pointB)
|
||||
return pointA < pointB;
|
||||
|
||||
i += tid1Cnt, j += tid2Cnt;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} sortCreateTIDs;
|
||||
|
||||
static void onMainChange(void *a)
|
||||
{
|
||||
if(ui::usrMenu->getSelected() < (int)data::users.size())
|
||||
if (ui::usrMenu->getSelected() < (int)data::users.size())
|
||||
{
|
||||
unsigned setUser = ui::usrMenu->getSelected();
|
||||
data::setUserIndex(setUser);
|
||||
|
|
@ -87,7 +87,7 @@ static void toEXT(void *a)
|
|||
|
||||
static void usrOptCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
usrOptPanel->closePanel();
|
||||
|
|
@ -99,7 +99,7 @@ static void usrOptCallback(void *a)
|
|||
|
||||
static void saveCreateCallback(void *a)
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_B:
|
||||
usrOptMenu->setActive(true);
|
||||
|
|
@ -126,32 +126,32 @@ static void usrOptSaveCreate(void *a)
|
|||
ui::updateInput();
|
||||
int sel = m->getSelected();
|
||||
bool closeUsrOpt = false;
|
||||
if(sel == devPos && deviceSaveMenu->getOptCount() > 0)
|
||||
if (sel == devPos && deviceSaveMenu->getOptCount() > 0)
|
||||
{
|
||||
deviceSaveMenu->setActive(true);
|
||||
deviceSavePanel->openPanel();
|
||||
closeUsrOpt = true;
|
||||
}
|
||||
else if(sel == bcatPos && bcatSaveMenu->getOptCount() > 0)
|
||||
else if (sel == bcatPos && bcatSaveMenu->getOptCount() > 0)
|
||||
{
|
||||
bcatSaveMenu->setActive(true);
|
||||
bcatSavePanel->openPanel();
|
||||
closeUsrOpt = true;
|
||||
}
|
||||
else if(sel == cachePos && cacheSaveMenu->getOptCount() > 0)
|
||||
else if (sel == cachePos && cacheSaveMenu->getOptCount() > 0)
|
||||
{
|
||||
cacheSaveMenu->setActive(true);
|
||||
cacheSavePanel->openPanel();
|
||||
closeUsrOpt = true;
|
||||
}
|
||||
else if(sel < devPos)
|
||||
else if (sel < devPos)
|
||||
{
|
||||
saveCreateMenu->setActive(true);
|
||||
saveCreatePanel->openPanel();
|
||||
closeUsrOpt = true;
|
||||
}
|
||||
|
||||
if(closeUsrOpt)
|
||||
if (closeUsrOpt)
|
||||
{
|
||||
usrOptMenu->setActive(false);
|
||||
usrOptPanel->closePanel();
|
||||
|
|
@ -166,11 +166,13 @@ static void usrOptDeleteAllUserSaves_t(void *a)
|
|||
int curUserIndex = data::getCurrentUserIndex();
|
||||
int devUser = ui::usrMenu->getOptPos(ui::getUICString("saveTypeMainMenu", 0));
|
||||
|
||||
for(data::userTitleInfo& tinf : u->titleInfo)
|
||||
for (data::userTitleInfo &tinf : u->titleInfo)
|
||||
{
|
||||
if(tinf.saveInfo.save_data_type != FsSaveDataType_System && (tinf.saveInfo.save_data_type != FsSaveDataType_Device || curUserIndex == devUser))
|
||||
if (tinf.saveInfo.save_data_type != FsSaveDataType_System &&
|
||||
(tinf.saveInfo.save_data_type != FsSaveDataType_Device || curUserIndex == devUser))
|
||||
{
|
||||
t->status->setStatus(ui::getUICString("threadStatusDeletingSaveData", 0), data::getTitleNameByTID(tinf.tid).c_str());
|
||||
t->status->setStatus(ui::getUICString("threadStatusDeletingSaveData", 0),
|
||||
data::getTitleNameByTID(tinf.tid).c_str());
|
||||
fsDeleteSaveDataFileSystemBySaveDataSpaceId(FsSaveDataSpaceId_User, tinf.saveInfo.save_data_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +184,12 @@ static void usrOptDeleteAllUserSaves_t(void *a)
|
|||
static void usrOptDeleteAllUserSaves(void *a)
|
||||
{
|
||||
data::user *u = data::getCurrentUser();
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(true, usrOptDeleteAllUserSaves_t, NULL, NULL, ui::getUICString("saveDataDeleteAllUser", 0), u->getUsername().c_str());
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(true,
|
||||
usrOptDeleteAllUserSaves_t,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("saveDataDeleteAllUser", 0),
|
||||
u->getUsername().c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +235,9 @@ static void createSaveData(void *a)
|
|||
|
||||
//Device, BCAT, and Cache are hardcoded user IDs in JKSV's data
|
||||
uint64_t tid = 0;
|
||||
switch(uid)
|
||||
switch (uid)
|
||||
{
|
||||
case 0://This is system
|
||||
case 0: //This is system
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
|
@ -262,20 +269,20 @@ static void usrOptCreateAllSaves_t(void *a)
|
|||
int devPos = ui::usrMenu->getOptPos(ui::getUICString("saveTypeMainMenu", 0));
|
||||
int bcatPos = ui::usrMenu->getOptPos(ui::getUICString("saveTypeMainMenu", 1));
|
||||
int sel = ui::usrMenu->getSelected();
|
||||
if(sel < devPos)
|
||||
if (sel < devPos)
|
||||
{
|
||||
AccountUid uid = u->getUID();
|
||||
for(unsigned i = 0; i < accSids.size(); i++)
|
||||
for (unsigned i = 0; i < accSids.size(); i++)
|
||||
fs::createSaveData(FsSaveDataType_Account, accSids[i], uid, t);
|
||||
}
|
||||
else if(sel == devPos)
|
||||
else if (sel == devPos)
|
||||
{
|
||||
for(unsigned i = 0; i < devSids.size(); i++)
|
||||
for (unsigned i = 0; i < devSids.size(); i++)
|
||||
fs::createSaveData(FsSaveDataType_Device, devSids[i], util::u128ToAccountUID(0), t);
|
||||
}
|
||||
else if(sel == bcatPos)
|
||||
else if (sel == bcatPos)
|
||||
{
|
||||
for(unsigned i = 0; i < bcatSids.size(); i++)
|
||||
for (unsigned i = 0; i < bcatSids.size(); i++)
|
||||
fs::createSaveData(FsSaveDataType_Bcat, bcatSids[i], util::u128ToAccountUID(0), t);
|
||||
}
|
||||
t->finished = true;
|
||||
|
|
@ -284,7 +291,12 @@ static void usrOptCreateAllSaves_t(void *a)
|
|||
static void usrOptCreateAllSaves(void *a)
|
||||
{
|
||||
data::user *u = data::getCurrentUser();
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(true, usrOptCreateAllSaves_t, NULL, NULL, ui::getUICString("confirmCreateAllSaveData", 0), u->getUsername().c_str());
|
||||
ui::confirmArgs *conf = ui::confirmArgsCreate(true,
|
||||
usrOptCreateAllSaves_t,
|
||||
NULL,
|
||||
NULL,
|
||||
ui::getUICString("confirmCreateAllSaveData", 0),
|
||||
u->getUsername().c_str());
|
||||
ui::confirm(conf);
|
||||
}
|
||||
|
||||
|
|
@ -302,20 +314,21 @@ static void initSaveCreateMenus()
|
|||
cacheSids.clear();
|
||||
|
||||
//Group into vectors to match
|
||||
for(auto& t : data::titles)
|
||||
for (auto &t : data::titles)
|
||||
{
|
||||
NacpStruct *nacp = &t.second.nacp;
|
||||
NacpStruct *nacp = &t.second.data.nacp;
|
||||
|
||||
if(nacp->user_account_save_data_size > 0)
|
||||
if (nacp->user_account_save_data_size > 0)
|
||||
accSids.push_back(t.first);
|
||||
|
||||
if(nacp->device_save_data_size > 0)
|
||||
if (nacp->device_save_data_size > 0)
|
||||
devSids.push_back(t.first);
|
||||
|
||||
if(nacp->bcat_delivery_cache_storage_size > 0)
|
||||
if (nacp->bcat_delivery_cache_storage_size > 0)
|
||||
bcatSids.push_back(t.first);
|
||||
|
||||
if(nacp->cache_storage_size > 0 || nacp->cache_storage_journal_size > 0 || nacp->cache_storage_data_and_journal_size_max > 0)
|
||||
if (nacp->cache_storage_size > 0 || nacp->cache_storage_journal_size > 0 ||
|
||||
nacp->cache_storage_data_and_journal_size_max > 0)
|
||||
cacheSids.push_back(t.first);
|
||||
}
|
||||
|
||||
|
|
@ -325,25 +338,25 @@ static void initSaveCreateMenus()
|
|||
std::sort(bcatSids.begin(), bcatSids.end(), sortCreateTIDs);
|
||||
std::sort(cacheSids.begin(), cacheSids.end(), sortCreateTIDs);
|
||||
|
||||
for(unsigned i = 0; i < accSids.size(); i++)
|
||||
for (unsigned i = 0; i < accSids.size(); i++)
|
||||
{
|
||||
saveCreateMenu->addOpt(NULL, data::getTitleNameByTID(accSids[i]));
|
||||
saveCreateMenu->optAddButtonEvent(i, HidNpadButton_A, createSaveData, NULL);
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < devSids.size(); i++)
|
||||
for (unsigned i = 0; i < devSids.size(); i++)
|
||||
{
|
||||
deviceSaveMenu->addOpt(NULL, data::getTitleNameByTID(devSids[i]));
|
||||
deviceSaveMenu->optAddButtonEvent(i, HidNpadButton_A, createSaveData, NULL);
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < bcatSids.size(); i++)
|
||||
for (unsigned i = 0; i < bcatSids.size(); i++)
|
||||
{
|
||||
bcatSaveMenu->addOpt(NULL, data::getTitleNameByTID(bcatSids[i]));
|
||||
bcatSaveMenu->optAddButtonEvent(i, HidNpadButton_A, createSaveData, NULL);
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < cacheSids.size(); i++)
|
||||
for (unsigned i = 0; i < cacheSids.size(); i++)
|
||||
{
|
||||
cacheSaveMenu->addOpt(NULL, data::getTitleNameByTID(cacheSids[i]));
|
||||
cacheSaveMenu->optAddButtonEvent(i, HidNpadButton_A, createSaveData, NULL);
|
||||
|
|
@ -373,7 +386,7 @@ void ui::usrInit()
|
|||
cacheSaveMenu->setActive(false);
|
||||
cacheSaveMenu->setCallback(saveCreateCallback, NULL);
|
||||
|
||||
for(data::user u : data::users)
|
||||
for (data::user u : data::users)
|
||||
{
|
||||
int usrPos = usrMenu->addOpt(u.getUserIcon(), u.getUsername());
|
||||
usrMenu->optAddButtonEvent(usrPos, HidNpadButton_A, toTTL, NULL);
|
||||
|
|
@ -399,7 +412,7 @@ void ui::usrInit()
|
|||
usrOptPanel = new ui::slideOutPanel(410, 720, 0, ui::SLD_RIGHT, usrOptPanelDraw);
|
||||
ui::registerPanel(usrOptPanel);
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
usrOptMenu->addOpt(NULL, ui::getUIString("userOptions", i));
|
||||
|
||||
//Dump All User Saves
|
||||
|
|
@ -444,9 +457,6 @@ void ui::usrExit()
|
|||
delete deviceSaveMenu;
|
||||
delete bcatSaveMenu;
|
||||
delete cacheSaveMenu;
|
||||
|
||||
SDL_DestroyTexture(sett);
|
||||
SDL_DestroyTexture(ext);
|
||||
}
|
||||
|
||||
void ui::usrRefresh()
|
||||
|
|
@ -456,27 +466,27 @@ void ui::usrRefresh()
|
|||
|
||||
void ui::usrUpdate()
|
||||
{
|
||||
if(usrMenu->getActive())
|
||||
if (usrMenu->getActive())
|
||||
{
|
||||
switch(ui::padKeysDown())
|
||||
switch (ui::padKeysDown())
|
||||
{
|
||||
case HidNpadButton_Y:
|
||||
ui::newThread(fs::dumpAllUsersAllSaves, NULL, fs::fileDrawFunc);
|
||||
break;
|
||||
|
||||
case HidNpadButton_X:
|
||||
{
|
||||
int cachePos = usrMenu->getOptPos(ui::getUIString("saveTypeMainMenu", 2));
|
||||
if (usrMenu->getSelected() <= cachePos)
|
||||
{
|
||||
int cachePos = usrMenu->getOptPos(ui::getUIString("saveTypeMainMenu", 2));
|
||||
if(usrMenu->getSelected() <= cachePos)
|
||||
{
|
||||
data::user *u = data::getCurrentUser();
|
||||
usrOptMenu->editOpt(0, NULL, ui::getUIString("userOptions", 0) + u->getUsername());
|
||||
usrOptMenu->setActive(true);
|
||||
usrMenu->setActive(false);
|
||||
usrOptPanel->openPanel();
|
||||
}
|
||||
data::user *u = data::getCurrentUser();
|
||||
usrOptMenu->editOpt(0, NULL, ui::getUIString("userOptions", 0) + u->getUsername());
|
||||
usrOptMenu->setActive(true);
|
||||
usrMenu->setActive(false);
|
||||
usrOptPanel->openPanel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
usrMenu->update();
|
||||
|
|
@ -489,6 +499,6 @@ void ui::usrUpdate()
|
|||
|
||||
void ui::usrDraw(SDL_Texture *target)
|
||||
{
|
||||
if(ui::mstate == USR_SEL)
|
||||
if (ui::mstate == USR_SEL)
|
||||
gfx::drawTextf(NULL, 18, usrHelpX, 673, &ui::txtCont, ui::getUICString("helpUser", 0));
|
||||
}
|
||||
|
|
|
|||
223
src/util.cpp
223
src/util.cpp
|
|
@ -1,40 +1,40 @@
|
|||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <sys/stat.h>
|
||||
#include <json-c/json.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "file.h"
|
||||
#include "data.h"
|
||||
#include "gfx.h"
|
||||
#include "util.h"
|
||||
#include "ui.h"
|
||||
#include "curlfuncs.h"
|
||||
#include "type.h"
|
||||
#include "cfg.h"
|
||||
#include "curlfuncs.h"
|
||||
#include "data.h"
|
||||
#include "file.h"
|
||||
#include "gfx.h"
|
||||
#include "type.h"
|
||||
#include "ui.h"
|
||||
#include "util.h"
|
||||
|
||||
static const uint32_t verboten[] = { L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'};
|
||||
static const uint32_t verboten[] = {L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'};
|
||||
|
||||
static bool isVerboten(const uint32_t& t)
|
||||
static bool isVerboten(const uint32_t &t)
|
||||
{
|
||||
for(unsigned i = 0; i < 13; i++)
|
||||
for (unsigned i = 0; i < 13; i++)
|
||||
{
|
||||
if(t == verboten[i])
|
||||
if (t == verboten[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void util::replaceStr(std::string& _str, const std::string& _find, const std::string& _rep)
|
||||
void util::replaceStr(std::string &_str, const std::string &_find, const std::string &_rep)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = _str.find(_find)) != _str.npos)
|
||||
while ((pos = _str.find(_find)) != _str.npos)
|
||||
_str.replace(pos, _find.length(), _rep);
|
||||
}
|
||||
|
||||
//Used to split version tag git
|
||||
static void getVersionFromTag(const std::string& tag, unsigned& _year, unsigned& _month, unsigned& _day)
|
||||
static void getVersionFromTag(const std::string &tag, unsigned &_year, unsigned &_month, unsigned &_day)
|
||||
{
|
||||
_month = strtoul(tag.substr(0, 2).c_str(), NULL, 10);
|
||||
_day = strtoul(tag.substr(3, 5).c_str(), NULL, 10);
|
||||
|
|
@ -45,13 +45,13 @@ static void getVersionFromTag(const std::string& tag, unsigned& _year, unsigned&
|
|||
typedef enum
|
||||
{
|
||||
SwkbdPosStart = 0,
|
||||
SwkbdPosEnd = 1
|
||||
SwkbdPosEnd = 1
|
||||
} SwkbdInitPos;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t read[0x32 / sizeof(uint16_t)];
|
||||
uint16_t word[0x32 / sizeof(uint16_t)];
|
||||
uint16_t read[0x32 / sizeof(uint16_t)];
|
||||
uint16_t word[0x32 / sizeof(uint16_t)];
|
||||
} dictWord;
|
||||
|
||||
void swkbdDictWordCreate(dictWord *w, const char *read, const char *word)
|
||||
|
|
@ -64,7 +64,7 @@ void swkbdDictWordCreate(dictWord *w, const char *read, const char *word)
|
|||
|
||||
uint32_t replaceChar(uint32_t c)
|
||||
{
|
||||
switch(c)
|
||||
switch (c)
|
||||
{
|
||||
case L'é':
|
||||
return 'e';
|
||||
|
|
@ -77,9 +77,9 @@ uint32_t replaceChar(uint32_t c)
|
|||
static inline void replaceCharCStr(char *_s, char _find, char _rep)
|
||||
{
|
||||
size_t strLength = strlen(_s);
|
||||
for(unsigned i = 0; i < strLength; i++)
|
||||
for (unsigned i = 0; i < strLength; i++)
|
||||
{
|
||||
if(_s[i] == _find)
|
||||
if (_s[i] == _find)
|
||||
_s[i] = _rep;
|
||||
}
|
||||
}
|
||||
|
|
@ -92,14 +92,28 @@ std::string util::getDateTime(int fmt)
|
|||
time(&raw);
|
||||
tm *Time = localtime(&raw);
|
||||
|
||||
switch(fmt)
|
||||
switch (fmt)
|
||||
{
|
||||
case DATE_FMT_YMD:
|
||||
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||
sprintf(ret,
|
||||
"%04d.%02d.%02d @ %02d.%02d.%02d",
|
||||
Time->tm_year + 1900,
|
||||
Time->tm_mon + 1,
|
||||
Time->tm_mday,
|
||||
Time->tm_hour,
|
||||
Time->tm_min,
|
||||
Time->tm_sec);
|
||||
break;
|
||||
|
||||
case DATE_FMT_YDM:
|
||||
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mday, Time->tm_mon + 1, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||
sprintf(ret,
|
||||
"%04d.%02d.%02d @ %02d.%02d.%02d",
|
||||
Time->tm_year + 1900,
|
||||
Time->tm_mday,
|
||||
Time->tm_mon + 1,
|
||||
Time->tm_hour,
|
||||
Time->tm_min,
|
||||
Time->tm_sec);
|
||||
break;
|
||||
|
||||
case DATE_FMT_HOYSTE:
|
||||
|
|
@ -107,7 +121,13 @@ std::string util::getDateTime(int fmt)
|
|||
break;
|
||||
|
||||
case DATE_FMT_JHK:
|
||||
sprintf(ret, "%04d%02d%02d_%02d%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min);
|
||||
sprintf(ret,
|
||||
"%04d%02d%02d_%02d%02d",
|
||||
Time->tm_year + 1900,
|
||||
Time->tm_mon + 1,
|
||||
Time->tm_mday,
|
||||
Time->tm_hour,
|
||||
Time->tm_min);
|
||||
break;
|
||||
|
||||
case DATE_FMT_ASC:
|
||||
|
|
@ -120,47 +140,47 @@ std::string util::getDateTime(int fmt)
|
|||
return std::string(ret);
|
||||
}
|
||||
|
||||
void util::copyDirListToMenu(const fs::dirList& d, ui::menu& m)
|
||||
void util::copyDirListToMenu(const fs::dirList &d, ui::menu &m)
|
||||
{
|
||||
m.reset();
|
||||
m.addOpt(NULL, ".");
|
||||
m.addOpt(NULL, "..");
|
||||
for(unsigned i = 0; i < d.getCount(); i++)
|
||||
for (unsigned i = 0; i < d.getCount(); i++)
|
||||
{
|
||||
if(d.isDir(i))
|
||||
if (d.isDir(i))
|
||||
m.addOpt(NULL, "D " + d.getItem(i));
|
||||
else
|
||||
m.addOpt(NULL, "F " + d.getItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
void util::removeLastFolderFromString(std::string& _path)
|
||||
void util::removeLastFolderFromString(std::string &_path)
|
||||
{
|
||||
unsigned last = _path.find_last_of('/', _path.length() - 2);
|
||||
_path.erase(last + 1, _path.length());
|
||||
}
|
||||
|
||||
size_t util::getTotalPlacesInPath(const std::string& _path)
|
||||
size_t util::getTotalPlacesInPath(const std::string &_path)
|
||||
{
|
||||
//Skip device
|
||||
size_t pos = _path.find_first_of('/'), ret = 0;
|
||||
while((pos = _path.find_first_of('/', ++pos)) != _path.npos)
|
||||
while ((pos = _path.find_first_of('/', ++pos)) != _path.npos)
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void util::trimPath(std::string& _path, uint8_t _places)
|
||||
void util::trimPath(std::string &_path, uint8_t _places)
|
||||
{
|
||||
size_t pos = _path.find_first_of('/');
|
||||
for(int i = 0; i < _places; i++)
|
||||
for (int i = 0; i < _places; i++)
|
||||
pos = _path.find_first_of('/', ++pos);
|
||||
_path = _path.substr(++pos, _path.npos);
|
||||
}
|
||||
|
||||
std::string util::safeString(const std::string& s)
|
||||
std::string util::safeString(const std::string &s)
|
||||
{
|
||||
std::string ret = "";
|
||||
for(unsigned i = 0; i < s.length(); )
|
||||
for (unsigned i = 0; i < s.length();)
|
||||
{
|
||||
uint32_t tmpChr = 0;
|
||||
ssize_t untCnt = decode_utf8(&tmpChr, (uint8_t *)&s.data()[i]);
|
||||
|
|
@ -169,87 +189,34 @@ std::string util::safeString(const std::string& s)
|
|||
|
||||
tmpChr = replaceChar(tmpChr);
|
||||
|
||||
if(isVerboten(tmpChr))
|
||||
if (isVerboten(tmpChr))
|
||||
ret += ' ';
|
||||
else if(!isASCII(tmpChr))
|
||||
else if (!isASCII(tmpChr))
|
||||
return ""; //return empty string so titledata::init defaults to titleID
|
||||
else
|
||||
ret += (char)tmpChr;
|
||||
}
|
||||
|
||||
//Check for spaces at end
|
||||
while(ret[ret.length() - 1] == ' ' || ret[ret.length() - 1] == '.')
|
||||
while (ret[ret.length() - 1] == ' ' || ret[ret.length() - 1] == '.')
|
||||
ret.erase(ret.length() - 1, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline std::string getTimeString(const uint32_t& _h, const uint32_t& _m)
|
||||
static inline std::string getTimeString(const uint32_t &_h, const uint32_t &_m)
|
||||
{
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%02d:%02d", _h, _m);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
std::string util::getInfoString(data::user& u, const uint64_t& tid)
|
||||
{
|
||||
data::titleInfo *tinfo = data::getTitleInfoByTID(tid);
|
||||
data::userTitleInfo *userTinfo = data::getCurrentUserTitleInfo();
|
||||
|
||||
std::string ret = tinfo->title + "\n";
|
||||
|
||||
ret += ui::getUICString("infoStatus", 0) + util::getIDStr(tid) + "\n";
|
||||
ret += ui::getUICString("infoStatus", 1) + util::getIDStr(userTinfo->saveInfo.save_data_id) + "\n";
|
||||
|
||||
uint32_t hours, mins;
|
||||
hours = userTinfo->playStats.playtimeMinutes / 60;
|
||||
mins = userTinfo->playStats.playtimeMinutes - (hours * 60);
|
||||
|
||||
ret += ui::getUICString("infoStatus", 2) + getTimeString(hours, mins) + "\n";
|
||||
ret += ui::getUICString("infoStatus", 3) + std::to_string(userTinfo->playStats.totalLaunches) + "\n";
|
||||
|
||||
|
||||
switch(userTinfo->saveInfo.save_data_type)
|
||||
{
|
||||
case FsSaveDataType_System:
|
||||
ret += ui::getUICString("saveDataTypeText", 0);
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Account:
|
||||
ret += ui::getUICString("saveDataTypeText", 1);
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
ret += ui::getUICString("saveDataTypeText", 2);
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
ret += ui::getUICString("saveDataTypeText", 3);
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Temporary:
|
||||
ret += ui::getUICString("saveDataTypeText", 4);
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Cache:
|
||||
{
|
||||
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||
ret += ui::getUICString("saveDataTypeText", 5);
|
||||
ret += ui::getUICString("saveDataIndexText", 0) + std::to_string(d->saveInfo.save_data_index) + "\n";
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_SystemBcat:
|
||||
ret += ui::getUICString("saveDataTypeText", 6);
|
||||
break;
|
||||
}
|
||||
|
||||
ret += u.getUsername();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string util::getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[])
|
||||
std::string util::getStringInput(SwkbdType _type,
|
||||
const std::string &def,
|
||||
const std::string &head,
|
||||
size_t maxLength,
|
||||
unsigned dictCnt,
|
||||
const std::string dictWords[])
|
||||
{
|
||||
SwkbdConfig swkbd;
|
||||
swkbdCreate(&swkbd, dictCnt);
|
||||
|
|
@ -263,10 +230,10 @@ std::string util::getStringInput(SwkbdType _type, const std::string& def, const
|
|||
swkbdConfigSetKeySetDisableBitmask(&swkbd, SwkbdKeyDisableBitmask_Backslash | SwkbdKeyDisableBitmask_Percent);
|
||||
swkbdConfigSetDicFlag(&swkbd, 1);
|
||||
|
||||
if(dictCnt > 0)
|
||||
if (dictCnt > 0)
|
||||
{
|
||||
dictWord words[dictCnt];
|
||||
for(unsigned i = 0; i < dictCnt; i++)
|
||||
for (unsigned i = 0; i < dictCnt; i++)
|
||||
swkbdDictWordCreate(&words[i], dictWords[i].c_str(), dictWords[i].c_str());
|
||||
|
||||
swkbdConfigSetDictionary(&swkbd, (SwkbdDictWord *)words, dictCnt);
|
||||
|
|
@ -280,25 +247,25 @@ std::string util::getStringInput(SwkbdType _type, const std::string& def, const
|
|||
return std::string(out);
|
||||
}
|
||||
|
||||
std::string util::getExtensionFromString(const std::string& get)
|
||||
std::string util::getExtensionFromString(const std::string &get)
|
||||
{
|
||||
size_t ext = get.find_last_of('.');
|
||||
if(ext != get.npos)
|
||||
if (ext != get.npos)
|
||||
return get.substr(ext + 1, get.npos);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string util::getFilenameFromPath(const std::string& get)
|
||||
std::string util::getFilenameFromPath(const std::string &get)
|
||||
{
|
||||
size_t nameStart = get.find_last_of('/');
|
||||
if(nameStart != get.npos)
|
||||
if (nameStart != get.npos)
|
||||
return get.substr(nameStart + 1, get.npos);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string util::generateAbbrev(const uint64_t& tid)
|
||||
std::string util::generateAbbrev(uint64_t tid)
|
||||
{
|
||||
data::titleInfo *tmp = data::getTitleInfoByTID(tid);
|
||||
size_t titleLength = tmp->safeTitle.length();
|
||||
|
|
@ -309,23 +276,23 @@ std::string util::generateAbbrev(const uint64_t& tid)
|
|||
|
||||
std::string ret;
|
||||
char *tok = strtok(temp, " ");
|
||||
while(tok)
|
||||
while (tok)
|
||||
{
|
||||
if(isASCII(tok[0]))
|
||||
if (isASCII(tok[0]))
|
||||
ret += tok[0];
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void util::stripChar(char _c, std::string& _s)
|
||||
void util::stripChar(char _c, std::string &_s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = _s.find(_c)) != _s.npos)
|
||||
while ((pos = _s.find(_c)) != _s.npos)
|
||||
_s.erase(pos, 1);
|
||||
}
|
||||
|
||||
void util::replaceButtonsInString(std::string& rep)
|
||||
void util::replaceButtonsInString(std::string &rep)
|
||||
{
|
||||
replaceStr(rep, "[A]", "\ue0e0");
|
||||
replaceStr(rep, "[B]", "\ue0e1");
|
||||
|
|
@ -348,9 +315,9 @@ void util::replaceButtonsInString(std::string& rep)
|
|||
|
||||
SDL_Texture *util::createIconGeneric(const char *txt, int fontSize, bool clearBack)
|
||||
{
|
||||
SDL_Texture *ret = SDL_CreateTexture(gfx::render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, 256, 256);
|
||||
SDL_Texture *ret = gfx::texMgr->textureCreate(256, 256);
|
||||
SDL_SetRenderTarget(gfx::render, ret);
|
||||
if(clearBack)
|
||||
if (clearBack)
|
||||
{
|
||||
SDL_SetRenderDrawColor(gfx::render, ui::rectLt.r, ui::rectLt.g, ui::rectLt.b, ui::rectLt.a);
|
||||
SDL_RenderClear(gfx::render);
|
||||
|
|
@ -368,7 +335,7 @@ SDL_Texture *util::createIconGeneric(const char *txt, int fontSize, bool clearBa
|
|||
|
||||
void util::sysBoost()
|
||||
{
|
||||
if(R_FAILED(clkrstInitialize()))
|
||||
if (R_FAILED(clkrstInitialize()))
|
||||
return;
|
||||
|
||||
ClkrstSession cpu, gpu, ram;
|
||||
|
|
@ -388,7 +355,7 @@ void util::sysBoost()
|
|||
|
||||
void util::sysNormal()
|
||||
{
|
||||
if(R_FAILED(clkrstInitialize()))
|
||||
if (R_FAILED(clkrstInitialize()))
|
||||
return;
|
||||
|
||||
ClkrstSession cpu, gpu, ram;
|
||||
|
|
@ -396,7 +363,7 @@ void util::sysNormal()
|
|||
clkrstOpenSession(&gpu, PcvModuleId_GPU, 3);
|
||||
clkrstOpenSession(&ram, PcvModuleId_EMC, 3);
|
||||
|
||||
if(cfg::config["ovrClk"])
|
||||
if (cfg::config["ovrClk"])
|
||||
clkrstSetClockRate(&cpu, util::CPU_SPEED_1224MHz);
|
||||
else
|
||||
clkrstSetClockRate(&cpu, util::CPU_SPEED_1020MHz);
|
||||
|
|
@ -407,7 +374,6 @@ void util::sysNormal()
|
|||
clkrstCloseSession(&gpu);
|
||||
clkrstCloseSession(&ram);
|
||||
clkrstExit();
|
||||
|
||||
}
|
||||
|
||||
void util::checkForUpdate(void *a)
|
||||
|
|
@ -415,7 +381,7 @@ void util::checkForUpdate(void *a)
|
|||
threadInfo *t = (threadInfo *)a;
|
||||
t->status->setStatus(ui::getUICString("threadStatusCheckingForUpdate", 0));
|
||||
std::string gitJson = curlFuncs::getJSONURL(NULL, "https://api.github.com/repos/J-D-K/JKSV/releases/latest");
|
||||
if(gitJson.empty())
|
||||
if (gitJson.empty())
|
||||
{
|
||||
ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("onlineErrorConnecting", 0));
|
||||
t->finished = true;
|
||||
|
|
@ -429,7 +395,7 @@ void util::checkForUpdate(void *a)
|
|||
tagStr = json_object_get_string(tag);
|
||||
getVersionFromTag(tagStr, year, month, day);
|
||||
//This can throw false positives as is. need to fix sometime
|
||||
if(year > BLD_YEAR || month > BLD_MON || month > BLD_DAY)
|
||||
if (year > BLD_YEAR || month > BLD_MON || month > BLD_DAY)
|
||||
{
|
||||
t->status->setStatus(ui::getUICString("threadStatusDownloadingUpdate", 0));
|
||||
//dunno about NSP yet...
|
||||
|
|
@ -452,27 +418,16 @@ void util::checkForUpdate(void *a)
|
|||
t->finished = true;
|
||||
}
|
||||
|
||||
std::string util::getSizeString(const uint64_t& _size)
|
||||
std::string util::getSizeString(uint64_t _size)
|
||||
{
|
||||
char sizeStr[32];
|
||||
if(_size >= 0x40000000)
|
||||
if (_size >= 0x40000000)
|
||||
sprintf(sizeStr, "%.2fGB", (float)_size / 1024.0f / 1024.0f / 1024.0f);
|
||||
else if(_size >= 0x100000)
|
||||
else if (_size >= 0x100000)
|
||||
sprintf(sizeStr, "%.2fMB", (float)_size / 1024.0f / 1024.0f);
|
||||
else if(_size >= 0x400)
|
||||
else if (_size >= 0x400)
|
||||
sprintf(sizeStr, "%.2fKB", (float)_size / 1024.0f);
|
||||
else
|
||||
sprintf(sizeStr, "%lu Bytes", _size);
|
||||
return std::string(sizeStr);
|
||||
}
|
||||
|
||||
Result util::accountDeleteUser(AccountUid *uid)
|
||||
{
|
||||
Service *account = accountGetServiceSession();
|
||||
struct
|
||||
{
|
||||
AccountUid uid;
|
||||
} in = {*uid};
|
||||
|
||||
return serviceDispatchIn(account, 203, in);
|
||||
}
|
||||
|
|
|
|||
339
src/webdav.cpp
Normal file
339
src/webdav.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
#include "webdav.h"
|
||||
#include "fs.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
rfs::WebDav::WebDav(const std::string& origin, const std::string& username, const std::string& password)
|
||||
: origin(origin), username(username), password(password)
|
||||
{
|
||||
curl = curl_easy_init();
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT);
|
||||
if (!username.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
|
||||
|
||||
if (!password.empty())
|
||||
curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
rfs::WebDav::~WebDav() {
|
||||
if (curl) {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
}
|
||||
|
||||
bool rfs::WebDav::resourceExists(const std::string& id) {
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
// we expect id to be properly escaped and starting with a /
|
||||
std::string fullUrl = origin + id;
|
||||
|
||||
struct curl_slist *headers = NULL;
|
||||
headers = curl_slist_append(headers, "Depth: 0");
|
||||
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_CUSTOMREQUEST, "PROPFIND");
|
||||
curl_easy_setopt(local_curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(local_curl, CURLOPT_NOBODY, 1L); // do not include the response body
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
|
||||
curl_slist_free_all(headers); // free the custom headers
|
||||
|
||||
bool ret = false;
|
||||
if(res == CURLE_OK) {
|
||||
long response_code;
|
||||
curl_easy_getinfo(local_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if(response_code == 207) { // 207 Multi-Status is a successful response for PROPFIND
|
||||
ret = true;
|
||||
}
|
||||
} else {
|
||||
fs::logWrite("WebDav: directory exists failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
curl_easy_cleanup(local_curl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// parent ID can never be empty
|
||||
std::string rfs::WebDav::appendResourceToParentId(const std::string& resourceName, const std::string& parentId, bool isDir) {
|
||||
char *escaped = curl_easy_escape(curl, resourceName.c_str(), 0);
|
||||
// we always expect parent to be properly URL encoded.
|
||||
std::string ret = parentId + std::string(escaped) + (isDir ? "/" : "");
|
||||
curl_free(escaped);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool rfs::WebDav::createDir(const std::string& dirName, const std::string& parentId) {
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
std::string urlPath = appendResourceToParentId(dirName, parentId, true);
|
||||
std::string fullUrl = origin + urlPath;
|
||||
|
||||
fs::logWrite("WebDav: Create directory at %s\n", fullUrl.c_str());
|
||||
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_CUSTOMREQUEST, "MKCOL");
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
fs::logWrite("WebDav: directory creation failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
bool ret = res == CURLE_OK;
|
||||
|
||||
curl_easy_cleanup(local_curl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// we always expect parent to be properly URL encoded.
|
||||
void rfs::WebDav::uploadFile(const std::string& filename, const std::string& parentId, curlFuncs::curlUpArgs *_upload) {
|
||||
std::string fileId = appendResourceToParentId(filename, parentId, false);
|
||||
updateFile(fileId, _upload);
|
||||
}
|
||||
void rfs::WebDav::updateFile(const std::string& _fileID, curlFuncs::curlUpArgs *_upload) {
|
||||
// for webdav, same as upload
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
std::string fullUrl = origin + _fileID;
|
||||
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_UPLOAD, 1L); // implicit PUT
|
||||
curl_easy_setopt(local_curl, CURLOPT_READFUNCTION, curlFuncs::readDataFile);
|
||||
curl_easy_setopt(local_curl, CURLOPT_READDATA, _upload);
|
||||
curl_easy_setopt(local_curl, CURLOPT_UPLOAD_BUFFERSIZE, UPLOAD_BUFFER_SIZE);
|
||||
curl_easy_setopt(local_curl, CURLOPT_UPLOAD, 1);
|
||||
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
if(res != CURLE_OK) {
|
||||
fs::logWrite("WebDav: file upload failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
curl_easy_cleanup(local_curl); // Clean up the CURL handle
|
||||
}
|
||||
void rfs::WebDav::downloadFile(const std::string& _fileID, curlFuncs::curlDlArgs *_download) {
|
||||
//Downloading is threaded because it's too slow otherwise
|
||||
dlWriteThreadStruct dlWrite;
|
||||
dlWrite.cfa = _download;
|
||||
|
||||
Thread writeThread;
|
||||
threadCreate(&writeThread, writeThread_t, &dlWrite, NULL, 0x8000, 0x2B, 2);
|
||||
|
||||
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
std::string fullUrl = origin + _fileID;
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_WRITEFUNCTION, writeDataBufferThreaded);
|
||||
curl_easy_setopt(local_curl, CURLOPT_WRITEDATA, &dlWrite);
|
||||
threadStart(&writeThread);
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
|
||||
// Copied from gd.cpp implementation.
|
||||
// TODO: Not sure how a thread helps if this parent waits here.
|
||||
threadWaitForExit(&writeThread);
|
||||
threadClose(&writeThread);
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
fs::logWrite("WebDav: file download failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
curl_easy_cleanup(local_curl);
|
||||
}
|
||||
void rfs::WebDav::deleteFile(const std::string& _fileID) {
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
std::string fullUrl = origin + _fileID;
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
if(res != CURLE_OK) {
|
||||
fs::logWrite("WebDav: file deletion failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
curl_easy_cleanup(local_curl);
|
||||
}
|
||||
|
||||
bool rfs::WebDav::dirExists(const std::string& dirName, const std::string& parentId) {
|
||||
std::string urlPath = getDirID(dirName, parentId);
|
||||
return resourceExists(urlPath);
|
||||
}
|
||||
|
||||
bool rfs::WebDav::fileExists(const std::string& filename, const std::string& parentId) {
|
||||
std::string urlPath = appendResourceToParentId(filename, parentId, false);
|
||||
return resourceExists(urlPath);
|
||||
}
|
||||
|
||||
std::string rfs::WebDav::getFileID(const std::string& filename, const std::string& parentId) {
|
||||
return appendResourceToParentId(filename, parentId, false);
|
||||
}
|
||||
|
||||
std::string rfs::WebDav::getDirID(const std::string& dirName, const std::string& parentId) {
|
||||
return appendResourceToParentId(dirName, parentId, true);
|
||||
}
|
||||
|
||||
std::vector<rfs::RfsItem> rfs::WebDav::getListWithParent(const std::string& _parentId) {
|
||||
std::vector<rfs::RfsItem> list;
|
||||
|
||||
CURL* local_curl = curl_easy_duphandle(curl);
|
||||
|
||||
// we expect _resource to be properly escaped
|
||||
std::string fullUrl = origin + _parentId;
|
||||
|
||||
struct curl_slist *headers = NULL;
|
||||
headers = curl_slist_append(headers, "Depth: 1");
|
||||
|
||||
std::string responseString;
|
||||
|
||||
curl_easy_setopt(local_curl, CURLOPT_URL, fullUrl.c_str());
|
||||
curl_easy_setopt(local_curl, CURLOPT_CUSTOMREQUEST, "PROPFIND");
|
||||
curl_easy_setopt(local_curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(local_curl, CURLOPT_WRITEFUNCTION, curlFuncs::writeDataString);
|
||||
curl_easy_setopt(local_curl, CURLOPT_WRITEDATA, &responseString);
|
||||
|
||||
CURLcode res = curl_easy_perform(local_curl);
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
long response_code;
|
||||
curl_easy_getinfo(local_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if(response_code == 207) { // 207 Multi-Status is a successful response for PROPFIND
|
||||
fs::logWrite("WebDav: Response from WebDav. Parsing.\n");
|
||||
std::vector<rfs::RfsItem> items = parseXMLResponse(responseString);
|
||||
|
||||
// insert into array
|
||||
// TODO: Filter for zip?
|
||||
list.insert(list.end(), items.begin(), items.end());
|
||||
}
|
||||
} else {
|
||||
fs::logWrite("WebDav: directory listing failed: %s\n", curl_easy_strerror(res));
|
||||
}
|
||||
|
||||
curl_slist_free_all(headers); // free the custom headers
|
||||
curl_easy_cleanup(local_curl);
|
||||
return list;
|
||||
}
|
||||
|
||||
// Helper
|
||||
std::string rfs::WebDav::getNamespacePrefix(tinyxml2::XMLElement* root, const std::string& nsURI) {
|
||||
for(const tinyxml2::XMLAttribute* attr = root->FirstAttribute(); attr; attr = attr->Next()) {
|
||||
std::string name = attr->Name();
|
||||
std::string value = attr->Value();
|
||||
if(value == nsURI) {
|
||||
auto pos = name.find(':');
|
||||
if(pos != std::string::npos) {
|
||||
return name.substr(pos + 1);
|
||||
} else {
|
||||
return ""; // Default namespace (no prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""; // No namespace found
|
||||
}
|
||||
|
||||
std::vector<rfs::RfsItem> rfs::WebDav::parseXMLResponse(const std::string& xml) {
|
||||
std::vector<RfsItem> items;
|
||||
tinyxml2::XMLDocument doc;
|
||||
|
||||
if(doc.Parse(xml.c_str()) != tinyxml2::XML_SUCCESS) {
|
||||
fs::logWrite("WebDav: Failed to parse XML from Server\n");
|
||||
return items;
|
||||
}
|
||||
|
||||
// Get the root element
|
||||
tinyxml2::XMLElement *root = doc.RootElement();
|
||||
std::string nsPrefix = getNamespacePrefix(root, "DAV:");
|
||||
nsPrefix = !nsPrefix.empty() ? nsPrefix + ":" : nsPrefix; // Append colon if non-empty
|
||||
|
||||
fs::logWrite("WebDav: Parsing response, using prefix: %s\n", nsPrefix.c_str());
|
||||
|
||||
// Loop through the responses
|
||||
tinyxml2::XMLElement* responseElem = root->FirstChildElement((nsPrefix + "response").c_str());
|
||||
|
||||
std::string parentId;
|
||||
|
||||
while (responseElem) {
|
||||
RfsItem item;
|
||||
item.size = 0;
|
||||
|
||||
tinyxml2::XMLElement* hrefElem = responseElem->FirstChildElement((nsPrefix + "href").c_str());
|
||||
if (hrefElem) {
|
||||
std::string hrefText = hrefElem->GetText();
|
||||
// href can be absolute URI or relative reference. ALWAYS convert to relative reference
|
||||
if(hrefText.find(origin) == 0) {
|
||||
hrefText = hrefText.substr(origin.length());
|
||||
}
|
||||
item.id = hrefText;
|
||||
item.parent = parentId;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* propstatElem = responseElem->FirstChildElement((nsPrefix + "propstat").c_str());
|
||||
if (propstatElem) {
|
||||
tinyxml2::XMLElement* propElem = propstatElem->FirstChildElement((nsPrefix + "prop").c_str());
|
||||
if (propElem) {
|
||||
tinyxml2::XMLElement* displaynameElem = propElem->FirstChildElement((nsPrefix + "displayname").c_str());
|
||||
if (displaynameElem) {
|
||||
item.name = displaynameElem->GetText();
|
||||
} else {
|
||||
// Fallback to name from href
|
||||
item.name = getDisplayNameFromURL(item.id);
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* resourcetypeElem = propElem->FirstChildElement((nsPrefix + "resourcetype").c_str());
|
||||
if (resourcetypeElem) {
|
||||
item.isDir = resourcetypeElem->FirstChildElement((nsPrefix + "collection").c_str()) != nullptr;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* contentLengthElem = propElem->FirstChildElement((nsPrefix + "getcontentlength").c_str());
|
||||
if (contentLengthElem) {
|
||||
const char* sizeStr = contentLengthElem->GetText();
|
||||
if (sizeStr) {
|
||||
item.size = std::stoi(sizeStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
responseElem = responseElem->NextSiblingElement((nsPrefix + "response").c_str());
|
||||
|
||||
// first Item is always the parent.
|
||||
if (parentId.empty()) {
|
||||
parentId = item.id;
|
||||
continue; // do not push parent to list (we are only interested in the children)
|
||||
}
|
||||
|
||||
items.push_back(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
// Function to extract and URL decode the filename from the URL
|
||||
std::string rfs::WebDav::getDisplayNameFromURL(const std::string &url) {
|
||||
// Find the position of the last '/'
|
||||
size_t pos = url.find_last_of('/');
|
||||
if (pos == std::string::npos) {
|
||||
// If '/' is not found, return the whole URL as it is
|
||||
return url;
|
||||
}
|
||||
|
||||
// Extract the filename from the URL
|
||||
std::string encodedFilename = url.substr(pos + 1);
|
||||
|
||||
// URL decode the filename
|
||||
int outlength;
|
||||
char *decodedFilename = curl_easy_unescape(curl, encodedFilename.c_str(), encodedFilename.length(), &outlength);
|
||||
std::string result(decodedFilename, outlength);
|
||||
|
||||
// Free the memory allocated by curl_easy_unescape
|
||||
curl_free(decodedFilename);
|
||||
|
||||
return result;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user