Compare commits

..

106 Commits

Author SHA1 Message Date
J-D-K
51da0fd72f Cleanup compiler warnings. Bump version. 2025-08-25 09:00:28 -04:00
J-D-K
27ff070b7a Update some restore logic. Ensure save is wiped before restore again. 2025-08-24 19:17:10 -04:00
J-D-K
fd469c057d Typo fix. 2025-08-24 17:00:59 -04:00
J-D-K
6166856b5f Fix a couple bugs and mistakes. 2025-08-22 18:42:20 -04:00
J-D-K
01a7b8467d Output path fixes. 2025-08-22 17:22:56 -04:00
J-D-K
68fcf79cf5 Remove debug line. 2025-08-22 09:08:52 -04:00
J-D-K
bb26eb98ec Implement Trash option, update fslib for bug fix. 2025-08-22 09:07:33 -04:00
J-D-K
3b52b82e38 Update README a little bit. 2025-08-21 20:54:18 -04:00
J-D-K
e394bdbbf3 Update libs, remove remote instructions until I have more time to deal with Google's shenanigans. 2025-08-21 19:06:49 -04:00
J-D-K
babfcebfa5 Update Google Drive instructions for rewrite code. 2025-08-20 21:26:04 -04:00
J-D-K
2bb57ccb43 Bump build date for tomorrow. 2025-08-20 18:58:53 -04:00
J-D-K
280e5d6cb9 Fix init order so ForceEnglish option actually works. 2025-08-20 18:56:27 -04:00
J-D-K
2a48b18b49 Add 'System' type user toggles. 2025-08-20 15:38:01 -04:00
J-D-K
6bc3305340 Update fslib for range based Directory loops. 2025-08-20 12:39:29 -04:00
J-D-K
951707b372 Minor tweaks. 2025-08-18 21:58:56 -04:00
J-D-K
c76ae60092 Update language files. 2025-08-18 21:30:32 -04:00
J-D-K
e025901b0b Implement changing working directory. 2025-08-18 21:09:11 -04:00
J-D-K
e52e01a306 Update fslib, build date. 2025-08-18 18:53:21 -04:00
J-D-K
05b447e641 Revisions 2025-08-18 14:31:51 -04:00
J-D-K
fe30b48d59 Revised ConfigContext, remove texture purging from SDLLib for now. 2025-08-18 10:23:09 -04:00
J-D-K
1caec25d2a Revisions, SVI import fix. 2025-08-16 18:58:32 -04:00
J-D-K
6054a3a1f6 Update Language files. 2025-08-15 16:51:29 -04:00
J-D-K
8b6555bb22 Uncripple loading screen and pray. 2025-08-15 16:18:41 -04:00
J-D-K
ba6ad3f20d Add DataContext for data cleanup, add cache invalidation at boot. 2025-08-15 15:43:03 -04:00
J-D-K
74892e169f Band-aid fix for Auto-Backup 2025-08-14 12:01:37 -04:00
J-D-K
29e5dc66d2 Update README. Use PNGShot for superior screenshot. 2025-08-14 09:34:03 -04:00
J-D-K
d5c1e278fa Add strings for loading screen. 2025-08-13 20:08:28 -04:00
J-D-K
6e8e426fb5 Add loading screen. 2025-08-13 19:44:22 -04:00
J-D-K
14d9b7ffe1 Commit before major surgery. 2025-08-11 01:16:57 -04:00
J-D-K
25f61b8a54 Small fixes and tweaks. 2025-08-09 08:27:10 -04:00
J-D-K
2f2191bc15 WebDav fixes and tweaks. 2025-08-08 16:07:55 -04:00
J-D-K
7d9a294745 Add MessageState, tweak PopMessage again, add git templates. 2025-08-07 17:41:58 -04:00
J-D-K
5f4badf716 Tweak Y coord. 2025-08-06 22:09:21 -04:00
J-D-K
778f9582b6 Add light colored dialog so PopMessages contrast better. 2025-08-06 21:57:49 -04:00
J-D-K
22f569c36d Make PopMessage expand with typing text because it looks cool. 2025-08-06 21:25:23 -04:00
J-D-K
b4bc12a985 Change ui::PopMessage behavior. 2025-08-06 19:45:47 -04:00
J-D-K
884f1a36f9 Refactoring, fixes. 2025-08-06 19:35:23 -04:00
J-D-K
211f55fdaf Fix Google Drive bug. 2025-08-01 22:17:27 -04:00
J-D-K
dd013c892c Use ChatGPT to update string files. 2025-08-01 21:54:22 -04:00
J-D-K
225dc954f1 Add backup all with Auto-upload support. To do: Cleanup. 2025-08-01 21:31:16 -04:00
J-D-K
5364e59da8 Commit before the great template war. 2025-08-01 19:53:50 -04:00
J-D-K
8172e59ccf Add delete_all_remote_backups and some refactoring. 2025-08-01 13:53:57 -04:00
J-D-K
73af5ffe7e Fix menu scroll bug. Revisions. 2025-07-30 08:41:05 -04:00
J-D-K
42d7deb210 Fixes and tweaks. 2025-07-29 22:46:04 -04:00
J-D-K
b12b52bf38 Add fs::ScopedSaveMount 2025-07-29 22:16:43 -04:00
J-D-K
040329b256 Refactor TextTitleSelect 2025-07-29 19:45:46 -04:00
J-D-K
58e6217e73 Tweak SlidePanel behavior. 2025-07-29 19:06:48 -04:00
J-D-K
bc3f028082 Start updating rewrite readme. 2025-07-28 21:46:34 -04:00
J-D-K
e08984bb3f Tweak 2025-07-28 18:51:34 -04:00
J-D-K
b9b3acf7f2 Debugging everywhere! 2025-07-28 18:28:11 -04:00
J-D-K
721585c117 Auto-upload almost working. 2025-07-25 21:36:58 -04:00
J-D-K
851d969e9c More refactoring. Start to completely implement remote storage. 2025-07-24 10:07:57 -04:00
J-D-K
35825a6bb5 Add MiniZip wrappers. 2025-07-20 18:05:36 -04:00
J-D-K
23af517342 Add MiniZip wrapper 2025-07-20 16:17:16 -04:00
J-D-K
4874d0c120 More refactoring. Add error:: 2025-07-20 15:26:03 -04:00
J-D-K
3d5ddc8679 Begin BackupMenuState Refactor. 2025-07-18 16:53:22 -04:00
J-D-K
d0c032710d Refactor MainMenuState 2025-07-18 12:01:43 -04:00
J-D-K
8083ab30bd Refactor TitleSelectState 2025-07-18 11:20:43 -04:00
J-D-K
2564dcbd6d Refactor ExtrasMenu code (So far) 2025-07-18 10:29:02 -04:00
J-D-K
6d3b1b72b5 Refactor ConfirmState code. 2025-07-18 09:47:13 -04:00
J-D-K
2860294d86 Update BaseState logic. 2025-07-18 06:25:14 -04:00
J-D-K
6af4bed9fa Change AppState to BaseState. Update headers. 2025-07-18 06:22:55 -04:00
J-D-K
f7163f8dd7 Fix *nix case sensitivity building issues. 2025-07-05 14:54:43 -04:00
J-D-K
59c51a88ce Remove debugging line. 2025-07-04 15:10:42 -04:00
J-D-K
b1b9ce2959 Fix WebDav basepath/root 2025-07-04 15:04:13 -04:00
J-D-K
a7413c3e75 Removing debug header logging. 2025-07-04 13:45:07 -04:00
J-D-K
e27e0c8e51 remote::Storage just barely working. 2025-07-04 13:06:47 -04:00
J-D-K
e29db89706 Fix issue from unimplemented changes. 2025-06-21 08:24:29 -04:00
J-D-K
4a6d365860 Add StateManager singleton. 2025-06-21 08:09:07 -04:00
J-D-K
53c9a4633f Implement data reinitialization/cache rebuilding. 2025-06-19 17:31:15 -04:00
J-D-K
025c778011 Get Text mode working. 2025-06-19 17:17:35 -04:00
J-D-K
62c5097d6d Commit since everything seems like it's working. 2025-06-18 13:40:26 -04:00
J-D-K
5cab0d505b Reimplement Wii loading glyph for tasks. 2025-06-13 11:23:59 -04:00
J-D-K
5c0333c704 Fix restoring from folder backups. 2025-06-13 09:44:43 -04:00
J-D-K
71b4c1fbfa Implement processing save data meta files. 2025-06-12 20:46:55 -04:00
J-D-K
8dd16c12bc Implement/complete TitleInfoState 2025-06-12 16:25:22 -04:00
J-D-K
95ecbec3e4 Fix system save check. 2025-06-09 18:05:15 -04:00
J-D-K
5d14f26495 TitleOptionState: Add system save protections. 2025-06-09 14:58:10 -04:00
J-D-K
12deaa343c Remove strings for CPU boost mode. 2025-06-09 14:28:22 -04:00
J-D-K
69f728b29b Fix extending containers. Update and implement SVI files. 2025-06-09 14:22:24 -04:00
J-D-K
e355b99918 TitleOptions: Implement delete_save_data_from_system 2025-06-07 17:35:48 -04:00
J-D-K
75959d217f TitleOptions: Implement reset_save_data 2025-06-07 16:54:37 -04:00
J-D-K
ea8c653649 Implement Delete All Save Backups option for TitleOptions 2025-06-07 16:42:20 -04:00
J-D-K
b37413a51c Implement blacklisting in data and title options. 2025-06-07 16:33:54 -04:00
J-D-K
ffd614c25e Use boost mode because why not? 2025-06-07 15:46:37 -04:00
J-D-K
39747a0031 Basic, core stuff working AFAIK. 2025-06-07 15:34:28 -04:00
J-D-K
ecbeff1856 Data loading tweaks, fixes, and finishing touches. 2025-05-31 13:39:01 -04:00
J-D-K
34e4cdff87 Fix cache loading mistake. 2025-05-28 10:04:44 -04:00
J-D-K
758fa3c61a Base cache system. Still possibly WIP. 2025-05-28 09:39:53 -04:00
J-D-K
80cbb88a5a Add missing curl:: functions. 2025-04-02 22:11:36 -04:00
J-D-K
ec807c0ee8 Begin porting over new remote/Google Drive code. 2025-04-02 22:03:03 -04:00
J-D-K
6b2283d45b Start remote storage work. (Unbuild. Untested.) 2025-03-06 13:19:01 -05:00
J-D-K
11672043c3 Code style update. 2025-02-21 13:31:40 -05:00
J-D-K
88e66992ed Emergency push. 2025-01-31 19:45:44 -05:00
J-D-K
e894b39c63 Fix logic mistake. 2025-01-26 18:45:57 -05:00
J-D-K
57a9f0f4a6 Code revisions, implement ui::PopMessageManager. 2025-01-26 18:29:30 -05:00
J-D-K
95cc75c41d Minor work: Block home menu and exiting from tasks. 2025-01-09 21:21:05 -05:00
J-D-K
24e73971ff More work. 2025-01-06 17:51:00 -05:00
J-D-K
2395a8f1ff Progress is progress. 2024-12-24 16:54:31 -05:00
J-D-K
3fff5de46d Backup working mostly. 2024-12-22 12:39:31 -05:00
J-D-K
0b503b6324 Tonight's progress 2024-12-09 21:17:47 -05:00
J-D-K
da2d004ba6 Finalize text menu logic. Build with -Werror for better standards. 2024-12-09 11:20:17 -05:00
J-D-K
a62665313f Progress for today 2024-12-08 18:26:14 -05:00
J-D-K
53f67e94d8 Today's progress. 2024-12-07 21:17:09 -05:00
J-D-K
cf3cb7a219 Data partially implemented with no crashes. Getting too tired. 2024-12-06 20:44:47 -05:00
J-D-K
7816dab291 It's a start. 2024-12-05 20:04:47 -05:00
290 changed files with 18768 additions and 14174 deletions

View File

@ -1,136 +1,274 @@
---
Language: Cpp
BasedOnStyle: GNU
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Always
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: true
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Allman
BreakBeforeConceptDeclarations: Never
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
BreakTemplateDeclarations: Yes
ColumnLimit: 128
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
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: ''
- Regex: ^"(llvm|llvm-c|clang|clang-c)/
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: ^(<|"(gtest|gmock|isl|json)/)
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: .*
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: (Test)?$
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: true
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentPPDirectives: AfterHash
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: true
AtStartOfFile: true
LambdaBodyIndentation: Signature
LineEnding: LF
MacroBlockBegin: ""
MacroBlockEnd: ""
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 1000
PointerAlignment: Right
ReflowComments: false
SortIncludes: true
SortUsingDeclarations: true
QualifierAlignment: Left
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatementsExceptControlMacros
SpaceBeforeParensOptions:
AfterControlStatements: false
AfterForeachMacros: false
AfterFunctionDeclarationName: false
AfterFunctionDefinitionName: false
AfterIfMacros: false
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InConditionalStatements: false
InCStyleCasts: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never
...
TabWidth: 8
TableGenBreakInsideDAGArg: DontBreak
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
AllowAllConstructorInitializersOnNextLine: false
Language: Cpp

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
trim_trailing_whitespace = false

26
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,26 @@
---
name: Bug Report
about: Report a reproducible bug.
title: "Bug: "
labels: "bug"
---
**Describe the bug:**
Describe the bug or behavior to the best of your ability.
**Steps needed to reproduce:**
Describe what you were doing when the bug occurred.
**Expected behavior:**
Describe what you expected to happen.
**What happens instead:**
Describe what happened instead.
**Log file output:**
Check `sdmc:/config/JKSV/JKSV.log` for any meaningful output that may be useful in diagnosing the bug in question. The last lines should be the most valuable.

View File

@ -0,0 +1,10 @@
---
name: Feature Request
about: Request a feature for JKSV
title: "[Request]: "
labels: request
---
**Request:**
Describe what you would like JKSV to do that it can't already. Please note, adding different file hosting is dependent on how their API works and how much information they require to use it.

4
.gitignore vendored
View File

@ -2,7 +2,6 @@ build/
.vscode/
*.cbp
*.layout
.editorconfig
.idea/
cmake-build-debug/
@ -14,3 +13,6 @@ JKSV.nacp
JKSV.nro
JKSV.nso
JKSV.pfs0
#sometimes I give jksv to people and forget about this.
JKSV.zip

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "Libraries/FsLib"]
path = Libraries/FsLib
url = https://github.com/J-D-K/FsLib.git
[submodule "Libraries/SDLLib"]
path = Libraries/SDLLib
url = https://github.com/J-D-K/SDLLib.git

View File

@ -1,50 +0,0 @@
# 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})

1
Libraries/FsLib Submodule

@ -0,0 +1 @@
Subproject commit f948cecb43c4f1e089442a8c0fd58df7deefb955

1
Libraries/SDLLib Submodule

@ -0,0 +1 @@
Subproject commit fa7d0f456c78d7f94b8519260978419b564610b8

View File

@ -32,13 +32,14 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
TARGET := JKSV
BUILD := build
SOURCES := src src/ui src/fs src/gfx
SOURCES := source source/appstates source/config source/curl source/data source/fs \
source/logging source/remote source/strings source/sys source/tasks source/ui
DATA := data
INCLUDES := inc inc/ui inc/fs inc/gfx
INCLUDES := include ./Libraries/FsLib/Switch/FsLib/include ./Libraries/SDLLib/SDL/include
EXEFS_SRC := exefs_src
APP_TITLE := JKSV
APP_AUTHOR := JK
APP_VERSION := 11.5.2024
APP_VERSION := 08.25.2025
ROMFS := romfs
ICON := icon.jpg
@ -47,16 +48,20 @@ ICON := icon.jpg
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
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)
# NOTE: For some reason, devkitpro no longer has freetype-config included? Also, using pkg-config causes conflicts with
# my local pkg-config and I don't feel like dealing with CMake for all of this right now.
CFLAGS := $(INCLUDE) -D__SWITCH__ `sdl2-config --cflags` `curl-config --cflags`\
-g -Wall -O2 -ffunction-sections -ffast-math -fmax-errors=1 \
$(ARCH) $(DEFINES)
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=c++23
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 -ltinyxml2 -lnx
LIBS := ../Libraries/FsLib/Switch/FsLib/lib/libFsLib.a ../Libraries/SDLLib/SDL/lib/libSDL.a \
`sdl2-config --libs` -lfreetype -lharfbuzz `curl-config --libs` -lSDL2_image \
-lwebp -lpng -ljpeg -lz -lminizip -ljson-c -ltinyxml2 -lnx -lbz2 -lz
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@ -141,18 +146,30 @@ ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
.PHONY: $(BUILD) FsLib SDLLib clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
all: FsLib SDLLib $(BUILD)
$(BUILD):
$(BUILD): FsLib SDLLib
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
FsLib:
@$(MAKE) -C ./Libraries/FsLib/Switch/FsLib/ -j
#---------------------------------------------------------------------------------
SDLLib:
@$(MAKE) -C ./Libraries/SDLLib/SDL/ -j
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@$(MAKE) -C ./Libraries/FsLib/Switch/FsLib clean
@$(MAKE) -C ./Libraries/SDLLib/SDL clean
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf

189
README.MD
View File

@ -1,108 +1,105 @@
# JKSV
# JKSV Rewrite
JK's Save Manager Switch Edition.
<p align="center">
If you appreciate my work, or if JKSM or JKSV has gotten you out of a jam, please consider supporting development through a donation. Any contribution is appreciated, but never required! Creating and maintaining these tools takes a lot of time and effort.
</p>
<p align="center">
<a href='https://ko-fi.com/W7W51JF9W6' target='_blank'><img height='42' style='border:px;height:42px;' src='https://storage.ko-fi.com/cdn/kofi3.png?v=6' border='0' alt='Support me for the countless hours I poured into JKSV' /></a>
</p>
<img src="https://i.imgur.com/yLcTPzt.jpg"/>
<img src="https://i.imgur.com/1nMbJte.png" />
## Info
This started as a simple, straight port of my 3DS save manager I publicly released in 2016. Despite not originally wanting to take it too far, I continued working on it for fun when I can. Currently it can:
1. Dump and restore account save data.
2. Dump and restore device saves shared by all users (Games such as Animal Crossing).
3. Dump and restore BCAT Data.
4. Dump and restore cache Saves.
5. Dump system save data.
* Dumping this data is always enabled, but writing back needs to be enabled from the options menu. Writing to this can be very dangerous.
* Processes can be terminated from the Extras menu allowing you to open even more of these and explore more.
6. Export save data to folders like the orignal or compressed zip files to save space.
7. Upload and download save backups to [Google Drive](./REMOTE_INSTRUCTIONS.MD#gdrive) if it is configured.
8. Upload and download save backups to [WebDav](./REMOTE_INSTRUCTIONS.MD#webdav) if it is configured.
9. Create save data so the user no longer needs to boot games to import saves.
* Titles can be rescanned from the Extras menu. For example, if you insert a game card while JKSV is open, rescanning will load and add it to the save creation menu(s).
10. Export and use SVI files to create save data for titles not installed on your system. For games that detect other game saves to unlock content.
* SVI files are simply the title ID, NACP, and icon packed into a file. Placing them in `JKSV/svi` will load them as if they are any other game on your switch. They will appear in the save creation menus with the rest.
11. Extend save data containers to any size the user wants or automatically if the save cannot fit into the current one.
12. Delete save data from the system.
13. Reset save data as if the game was never launched.
14. Display stats and information about the game/save: Play time, launch count, title ID (TID), save ID(SID)/name of save file on user nand partition, etc.
15. Open and explore bis storage partitions via the Extras menu
* BIS Storage is opened inside a basic filebrowser. The partition's listing is on the left. Your SD is on the right.
* Only copying to SD and file properties work on BIS partitions. Writing to and deleting are disabled unless enabled like system save data.
16. Misc Extras:
* Ability to remove downloaded firmware updates from NAND. This is located under Extras.
* Terminating processes by [ID](https://switchbrew.org/wiki/Title_list#System_Modules). Allowing you to dump normally unopenable system archives.
* Mount by System Save [ID](https://switchbrew.org/wiki/Flash_Filesystem#System_Savegames). Normally used when the terminated process makes JKSV unable to rescan titles without the Switch crashing.
* Mount and open RomFS of process the homebrew menu takes over (if launched as NRO).
* Hold R while opening a game or applet with Atmosphere so the homebrew menu loads. Open JKSV and press minus and select **Mount Process RomFS**. The romfs of the app should appear in the browser along with your SD on the right.
## Rewritten from the ground up:
**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.**
JKSV's rewrite aims to accomplish the following:
- **Clean up and modernize the codebase:** The original master branch code became a mess over time and is extremely difficult to navigate and maintain.
- **Zero global variables:** Everything is encapsulated via interfaces. This alone makes the rewrite easier to work with and **much** less fragile in comparison to the original.
- **Improved error logging:** The master branch did an extremely poor job at logging errors. The rewrite corrects this greatly. Each error is logged with the file, line, and column making errors and bugs easier to track down.
- **Designed with translations in mind:** Unlike the original, translations weren't a feature that were tacked on later. This also made the original difficult to maintain.
- **Use standard formats:** JKSV originally used a custom file parser for data files. This has been removed in favor of JSON using libjson-c.
- **Uses [FsLib](https://github.com/J-D-K/FsLib) instead of [fs_dev.c](https://github.com/switchbrew/libnx/blob/master/nx/source/runtime/devices/fs_dev.c):**
- All file operations are handled using FsLib. FsLib is a C++ wrapper I wrote around libnx's FS API.
- FsLib also handles SD card redirection so libraries that depend on C standard I/O can read and write files to and from the SD card.
- **More convenient Google Drive login.**
- **Improve WebDav Support and code.**
- **Title Cache:** Titles found on the system are cached the same way [JKSM](https://github.com/J-D-K/JKSM) was. This improves boot time greatly on higher Switch firmware versions.
- The title cache is also **auto-invalidating**. No need to manually update it like JKSM on 3DS.
- **_Proper_ use of C++ features:**
- **Polymorphism and state patterns** instead of scattered global variables and logic.
- **Smart pointers** to enure no memory leaks can occur.
- **C APIs wrapped in smart pointers and classes** to ensure proper cleanup.
- **Templated task system** to reduce code duplication and complexity.
- **SDL textures** are all wrapped and managed by a central texture manager.
## Quick Guide
1. Main/User Menu
* A Selects the currently highlighted user and allows you to browse their titles.
* Y Dumps the save data for all users.
* X opens the sub menu of options for the currently highlighted user:
* Dump All for [X] dumps all of the saves for the highlighted user.
* Create save data opens a list of games found on your switch and will allow to create save data for them without needing to start the games.
* SVI files located in `JKSV/svi` will also be loaded and added to this list. These are to create save data for games not currently on the system. This is for games that search for other saves to unlock extra content.
* Cache saves require a cache index number to be created. This information can be found under information for a cache save when being exported.
* Create All Save Data creates save data for every title on your system for the selected user.
* Delete All Save Data deletes all save data for the selected user. This is permanent.
* Settings and Extras below.
JKSV's rewrite is a ground-up rewrite. It shares none of the original's code **at all**. **Everything** has been reworked and rewritten.
2. Title/Game Select
* A selects and opens the backup menu.
* Adding `.zip` to the end of your file name will force zip regardless of whether it's enabled or not.
* Y Favorites a title and pushes it to the top of your games.
* L and R jump forward down your games.
* X opens the title options menu:
* Information displays stats about the game for the current user.
* Blacklist adds the title to a list that is not displayed.
* Change Output Folder changes the folder to which the game's saves are written.
* Open in File Mode opens the save in a basic file browser.
* Delete All Save Backups deletes all of the backups for the current title.
* Reset Save Data wipes the save clean as if it was never run.
* Delete Save Data deletes the save data for the title from the system. This is the same as doing it from the Switch's data management setting.
* Extend Save Data extends the container for the current title. This is also done automatically if the save being imported is too large for the container.
* Different games have different limits. Most games do not need this at all. A few will take advantage of a larger container, others extend theirs at times and will need larger containers than created by default.
* Export SVI exports the data needed to create save data for the current title to `JKSV/ncap/[TID].svi`. This is just the title ID, NACP, and icon of the title. These can be used to create save data for games not installed on your system.
4. File Mode
* A opens directories.
* B goes back up a folder if possible.
* X opens a small menu of options for files and directories:
* Copy to [X] - Copies the currently selected item to the location opened on the other panel. Selecting the first `.` will use the directory opened as root to copy.
* Delete deletes the currently selected item.
* Rename renames the currently selected item.
* Make Dir creates a folder.
* Properties gets file size and directory size.
* ZL or ZR Change the controlled menu.
5. Settings
* Empty Trash Bin deletes all backups inside the `_TRASH_` folder. The trash bin feature can be disabled further down.
* Check For Updates checks github for updates to JKSV. This currently only updates the NRO release. Maybe NSP later.
* Set JKSV Save Output Folder allows you to set where JKSV's working directory is. Files and folders should be relocated for you.
* Edit Blacklisted Titles allows you to removed titles blacklisted from being shown.
* Delete All Save Backups wipes JKSV's folder of all save backups.
5. Extras
* SD To SD Browser opens the filebrowser with your SD open in both panels
* BIS: [X] opens partition [X] in the filebrowser.
* Remove Update deletes system updates downloaded from Nintendo and asks to reboot the system to get rid of the update nag.
* Terminate Process asks for a title ID to terminate.
* Mount System Save asks for the save ID to mount. This is for when JKSV is unable to rescan with a process terminated.
* Rescan Titles reloads save data information. This can be used to reload after a process is terminated or when options are changed.
* Mount Process RomFS opens the title's romfs that is taken over to launch the homebrew menu. This only works as an NRO. The NSP will only open JKSV's own RomFS.
* Backup JKSV folder writes the entire JKSV folder to a ZIP archive and places it in your JKSV folder.
**NOTE**: Press Plus to Exit JKSV. JKSV saves all config, favorites, and the blacklist when exited. Pressing the home button and closing that way will not allow this to take place.
## Features:
- **Supports all save data types used on Nintendo Switch:**
- **Account** (User)
- **System**
- **BCAT**
- **Device / Shared**
- **Temporary**
- **Cache**
- **System BCAT**
- **Export Options:**
- Standard, unpacked folders for editors.
- ZIP for better compatibility.
* **NOTE: ZIP is required and enforced for cloud backup!**
- **All At Once:**
- JKSV can backup all the save data on the system or for individual users at once.
- **Cloud Support:**
- If configured, JKSV currently supports:
- **Google Drive**
- **WebDav**
- JKSV can be configured to upload these automatically.
- **Create Save Data:**
- JKSV can create save data for any title on your system by **itself**. No need to start a game to import data for it.
- JKSV can create save data for every title found on your system **at once** for a single user.
- **SVI File Support:**
- Import and create save data for titles not installed on your system.
- **NOTE: These are not properly installed to the system. Use at your own discretion!**
- **Resize save data:**
- JKSV can extend save data to any size you would like. This is useful for games such as Minecraft.
- **Cleanup Tools**
- JKSV features functions and tools to make cleanup less of a chore compared to Nintendo's Data Management and confirmation prompts:
- Just hold A for three seconds to:
- Delete save data for individual games.
- Delete all save data for a single user at once.
- **Statistics:**
- View basic data and statistics about the save and title.
- **Customizable:**
- JKSV features many options to customize it to your liking.
- **Safety:**
- JKSV can require holding to complete destructive operations or be set to make quick automatic backups upon restoring backups.
## 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-tinyxml2 switch-libjson-c switch-libpng switch-libwebp switch-sdl2 switch-sdl2_gfx switch-sdl2_image switch-zlib`
1. Requires [devkitPro](https://devkitpro.org/) and [libnx](https://github.com/switchbrew/libnx).
2. Select Switch Development when installing or follow the instructions [here](https://devkitpro.org/wiki/Getting_Started) if you're not running Windows.
3. Install the following libraries using pacman and/or devkitpro's MSYS2 installation:
- **switch-bzip2**
- **switch-curl**
- **switch-freetype**
- **switch-harfbuzz**
- **switch-libjpeg-turbo**
- **switch-libjson-c**
- **switch-libpng**
- **switch-libwebp**
- **switch-sdl2**
- **switch-sdl2_image**
- **switch-tinyxml2**
- **switch-zlib**
4. Clone JKSV's directory using the command `git clone --recurse-submodules https://github.com/J-D-K/JKSV.git` so you clone the libraries I've built for JKSV included.
5. Press Y while in the Switch Homebrew menu, navigate to the directory JKSV was cloned into on your PC, and type `make -j send`. JKSV should build and nxlink will attempt to send it to your Switch.
## Credits and Thanks:
* [shared-font](https://github.com/switchbrew/switch-portlibs-examples) example by yellows8 for loading system font with Freetype. All other font handling code (converting to SDL2, resizing on the fly, checking for glyphs, cache, etc) is my own.
* [Iguniisu](https://github.com/igniscitrinus) for the icon.
* Uses graphics from [Twemoji](https://github.com/twitter/twemoji) covered by the [Creative Commons License](https://creativecommons.org/licenses/by/4.0/legalcode)
* [Leo](https://github.com/qazrfv1234) For the Traditional Chinese translation
* [JamePeng](https://github.com/JamePeng) For the Simplified Chinese translation.
* [shared-font](https://github.com/switchbrew/switch-portlibs-examples) example by yellows8 for loading system font with Freetype.
* Everyone that has contributed to translating and fixing the translations for JKSV.

View File

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

View File

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

View File

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

View File

@ -1,131 +0,0 @@
#pragma once
#include <switch.h>
#include <string>
#include <unordered_map>
#include <vector>
#include "gfx.h"
#define BLD_MON 5
#define BLD_DAY 28
#define BLD_YEAR 2025
namespace data
{
// Loads user + title info
void init();
void exit();
bool loadUsersTitles(bool clearUsers);
void sortUserTitles();
// Draws some stats to the upper left corner
void dispStats();
// Global stuff for all titles/saves
typedef struct
{
/// @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
typedef struct
{
// Makes it easier to grab id
uint64_t tid;
FsSaveDataInfo saveInfo;
PdmPlayStatistics playStats;
} userTitleInfo;
// Class to store user info + titles
class user
{
public:
user() = default;
user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName);
user(AccountUid _id, const std::string &_backupName, const std::string &_safeBackupName, SDL_Texture *img);
// Sets ID
void setUID(AccountUid _id);
// Assigns icon
void assignIcon(SDL_Texture *_icn)
{
userIcon = _icn;
}
// 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;
}
SDL_Texture *getUserIcon()
{
return userIcon;
}
void delIcon()
{
SDL_DestroyTexture(userIcon);
}
std::vector<data::userTitleInfo> titleInfo;
void addUserTitleInfo(const uint64_t &_tid,
const FsSaveDataInfo *_saveInfo,
const PdmPlayStatistics *_stats);
private:
AccountUid userID;
u128 uID128;
std::string username, userSafe;
// User icon
SDL_Texture *userIcon;
};
// User vector
extern std::vector<user> users;
// Title data/info map
extern std::unordered_map<uint64_t, data::titleInfo> titles;
// Sets/Retrieves current user/title
void setUserIndex(unsigned _sUser);
data::user *getCurrentUser();
unsigned getCurrentUserIndex();
void setTitleIndex(unsigned _sTitle);
data::userTitleInfo *getCurrentUserTitleInfo();
unsigned getCurrentUserTitleInfoIndex();
// Gets pointer to info that also has title + nacp
data::titleInfo *getTitleInfoByTID(const uint64_t &tid);
// More shortcut functions
std::string getTitleNameByTID(const uint64_t &tid);
std::string getTitleSafeNameByTID(const uint64_t &tid);
SDL_Texture *getTitleIconByTID(const uint64_t &tid);
int getTitleIndexInUser(const data::user &u, const uint64_t &tid);
extern SetLanguage sysLang;
} // namespace data

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,21 +0,0 @@
#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();
}

View File

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

View File

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

View File

@ -1,26 +0,0 @@
#pragma once
#include <SDL2/SDL.h>
#include "textureMgr.h"
namespace gfx
{
extern SDL_Renderer *render;
extern gfx::textureMgr *texMgr;
void init();
void exit();
void present();
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);
//Shortcuts for drawing
void texDraw(SDL_Texture *target, SDL_Texture *tex, int x, int y);
void texDrawStretch(SDL_Texture *target, SDL_Texture *tex, int x, int y, int w, int h);
void texDrawPart(SDL_Texture *target, SDL_Texture *tex, int srcX, int srcY, int srcW, int srcH, int dstX, int dstY);
void drawLine(SDL_Texture *target, const SDL_Color *c, int x1, int y1, int x2, int y2);
void drawRect(SDL_Texture *target, const SDL_Color *c, int x, int y, int w, int h);
void clearTarget(SDL_Texture *target, const SDL_Color *clear);
}

View File

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

View File

@ -1,53 +0,0 @@
#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);
}

View File

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

110
inc/ui.h
View File

@ -1,110 +0,0 @@
#pragma once
#include <switch.h>
#include <vector>
#include <string>
#include "data.h"
#include "gfx.h"
//ui headers - split up to keep a bit more organized
#include "ui/miscui.h"
#include "ui/uistr.h"
#include "ui/ttlview.h"
#include "ui/thrdProc.h"
#include "ui/sldpanel.h"
#include "ui/usr.h"
#include "ui/ttl.h"
#include "ui/fld.h"
#include "ui/sett.h"
#include "ui/ext.h"
#include "ui/fm.h"
enum menuState
{
USR_SEL,
TTL_SEL,
ADV_MDE,
EX_MNU,
OPT_MNU,
FIL_MDE
};
namespace ui
{
//Current menu/ui state
extern int mstate, prevState;
//Slide/animation scaling
extern float animScale;
//Loading glyph
extern const std::string loadGlyphArray[];
//pad data cause i don't know where else to put it
extern PadState pad;
extern HidTouchScreenState touchState;
static inline void updateInput() { touchState = {0}; padUpdate(&pad); hidGetTouchScreenStates(&touchState, 1); }
inline uint64_t padKeysDown() { return padGetButtonsDown(&pad); }
inline uint64_t padKeysHeld() { return padGetButtons(&pad); }
inline uint64_t padKeysUp() { return padGetButtonsUp(&pad); }
inline void changeState(int newState)
{
prevState = mstate;
mstate = newState;
}
//Holds theme set id
extern ColorSetId thmID;
//Both UI modes need access to this
extern std::string folderMenuInfo;
/*Colors
clearClr = color to clear buffer
txtCont = text that contrasts clearClr
txtDiag = text color for dialogs
*/
extern SDL_Color clearClr, transparent, txtCont, txtDiag, rectLt, rectSh, tboxClr, sideRect, divClr, heartColor, slidePanelColor;
//Textbox graphics
extern SDL_Texture *cornerTopLeft, *cornerTopRight, *cornerBottomLeft, *cornerBottomRight;
//Menu bounding
extern SDL_Texture *mnuTopLeft, *mnuTopRight, *mnuBotLeft, *mnuBotRight;
//Covers left and right of progress bar to fake being not a rectangle.
extern SDL_Texture *progCovLeft, *progCovRight, *diaBox;
//Side bar from Freebird. RIP.
extern SDL_Texture *sideBar;
//Sets colors and loads font for icon creation
void initTheme();
//Loads graphics and stuff
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);
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
//Just draws a screen and flips JIC boot takes long.
void showLoadScreen();
//Clears and draws general stuff used by multiple screens
void drawUI();
//switch case so we don't have problems with multiple main loops like 3DS
bool runApp();
void showPopMessage(int frameCount, const char *fmt, ...);
//Used for multiple menu functions/callback
void toTTL(void *);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,53 +0,0 @@
#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);
};
}

61
include/JKSV.hpp Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include "appstates/BaseState.hpp"
#include "sdl.hpp"
#include <memory>
#include <vector>
/// @brief Main application class.
class JKSV
{
public:
/// @brief Initializes JKSV. Initializes services.
JKSV();
/// @brief Exits services.
~JKSV();
/// @brief Returns if initializing was successful and JKSV is running.
/// @return True or false.
bool is_running() const;
/// @brief Runs JKSV's update routine.
void update();
/// @brief Runs JKSV's render routine.
void render();
private:
/// @brief Whether or not initialization was successful and JKSV is still running.
bool m_isRunning{};
/// @brief Whether or not to print the translation credits.
bool m_showTranslationInfo{};
/// @brief JKSV icon in upper left corner.
sdl::SharedTexture m_headerIcon{};
/// @brief Stores the translation string.
std::string m_translationInfo{};
/// @brief Stores the build string.
std::string m_buildString{};
/// @brief Initializes fslib and takes care of a few other things.
bool initialize_filesystem();
/// @brief Initializes the services JKSV uses.
bool initialize_services();
/// @brief Initializes SDL and loads the header icon.
bool initialize_sdl();
// Creates the needed directories on SD.
bool create_directories();
/// @brief Adds the text color changing characters.
void add_color_chars();
/// @brief Exits all services.
void exit_services();
};

52
include/JSON.hpp Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <json-c/json.h>
#include <memory>
#include <string>
namespace json
{
// Use this instead of default json_object
using Object = std::unique_ptr<json_object, decltype(&json_object_put)>;
// Use this instead of json_object_from_x. Pass the function and its arguments instead.
template <typename... Args>
static inline json::Object new_object(json_object *(*function)(Args...), Args... args)
{
return json::Object((*function)(args...), json_object_put);
}
/// @brief Inline wrapper function for getting a json object by it's key.
/// @param json json::Object to get the key from.
/// @param key Key to get.
/// @return json_object on success. NULL on failure.
static inline json_object *get_object(json::Object &json, std::string_view key)
{
return json_object_object_get(json.get(), key.data());
}
/// @brief Inline wrapper function to add an object to a json_object
/// @param json Json object to add an object to.
/// @param key Key of the object.
/// @param object Object to add to json.
/// @return True on success. False on failure.
static inline bool add_object(json::Object &json, std::string_view key, json_object *object)
{
return json_object_object_add(json.get(), key.data(), object) == 0;
}
/// @brief Returns the json string.
static inline const char *get_string(json::Object &json) { return json_object_get_string(json.get()); }
/// @brief Returns the length of the string. I find json_object_get_string_len is unreliable?
static inline int64_t length(json::Object &json)
{
const char *string = json_object_get_string(json.get());
return std::char_traits<char>::length(string);
}
/// @brief Returns the beginning for iterating.
static inline json_object_iterator iter_begin(json::Object &json) { return json_object_iter_begin(json.get()); }
/// @brief Returns the end for iterating.
static inline json_object_iterator iter_end(json::Object &json) { return json_object_iter_end(json.get()); }
} // namespace json

39
include/StateManager.hpp Normal file
View File

@ -0,0 +1,39 @@
#pragma once
#include "appstates/BaseState.hpp"
#include <memory>
#include <vector>
class StateManager
{
public:
// Singleton. No copying or constructing.
StateManager(const StateManager &) = delete;
StateManager(StateManager &&) = delete;
StateManager &operator=(const StateManager &) = delete;
StateManager &operator=(StateManager &&) = delete;
/// @brief Runs the state update routine.
static void update();
/// @brief Runs the state rendering routine(s);
static void render();
/// @brief Returns whether the back of the vector is a closable state.
static bool back_is_closable();
/// @brief Pushes a new state to the state vector.
/// @param newState Shared_ptr to state to push.
static void push_state(std::shared_ptr<BaseState> newState);
private:
/// @brief Private constructor so no constructing.
StateManager() = default;
/// @brief Returns a reference to the instance of StateManger.
/// @return Reference to state manager.
static StateManager &get_instance();
/// @brief This is the vector that holds the pointers to the states.
std::vector<std::shared_ptr<BaseState>> m_stateVector;
};

View File

@ -0,0 +1,169 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "data/data.hpp"
#include "fslib.hpp"
#include "remote/remote.hpp"
#include "sdl.hpp"
#include "sys/sys.hpp"
#include "ui/ui.hpp"
#include <memory>
/// @brief This is the state where the user can backup and restore saves.
class BackupMenuState final : public BaseState
{
public:
/// @brief Creates a new backup selection state.
/// @param user Pointer to currently selected user.
/// @param titleInfo Pointer to titleInfo of selected title.
/// @param saveType Save data type we're working with.
BackupMenuState(data::User *user, data::TitleInfo *titleInfo);
/// @brief Destructor. This is required even if it doesn't free or do anything.
~BackupMenuState() {};
/// @brief Creates and returns a new BackupMenuState.
static inline std::shared_ptr<BackupMenuState> create(data::User *user, data::TitleInfo *titleInfo)
{
return std::make_shared<BackupMenuState>(user, titleInfo);
}
/// @brief Creates and pushes a new BackupMenuState to the vector.
static inline std::shared_ptr<BackupMenuState> create_and_push(data::User *user, data::TitleInfo *titleInfo)
{
auto newState = BackupMenuState::create(user, titleInfo);
StateManager::push_state(newState);
return newState;
}
/// @brief Required. Inherited virtual function from AppState.
void update() override;
/// @brief Required. Inherited virtual function from AppState.
void render() override;
/// @brief Refreshes the directory listing and menu.
void refresh();
/// @brief Allows a spawned task to tell this class that it wrote save data to the system.
void save_data_written();
// clang-format off
enum class MenuEntryType
{
Null,
Local,
Remote
};
struct MenuEntry
{
MenuEntryType type{};
int index{};
};
struct DataStruct
{
data::User *user{};
data::TitleInfo *titleInfo{};
fslib::Path path{}; // This and
remote::Item *remoteItem{}; // this are set when needed.
BackupMenuState *spawningState{};
};
// clang-format on
// This makes some things elsewhere easier to type.
using TaskData = std::shared_ptr<BackupMenuState::DataStruct>;
private:
/// @brief Pointer to current user.
data::User *m_user{};
/// @brief Pointer to data for selected title.
data::TitleInfo *m_titleInfo{};
/// @brief Save data type we're working with.
FsSaveDataType m_saveType{};
/// @brief Path to the target directory of the title.
fslib::Path m_directoryPath{};
/// @brief Directory listing of the above.
fslib::Directory m_directoryListing{};
/// @brief Remote storage listing of the current parent.
remote::Storage::DirectoryListing m_remoteListing{};
/// @brief This is the scrolling text at the top.
std::shared_ptr<ui::TextScroll> m_titleScroll{};
/// @brief Variable that saves whether or not the filesystem has data in it.
bool m_saveHasData{};
/// @brief Data struct passed to functions.
std::shared_ptr<BackupMenuState::DataStruct> m_dataStruct{};
/// @brief This keeps track of the properties of the entries in the menu.
std::vector<BackupMenuState::MenuEntry> m_menuEntries{};
/// @brief This is a pointer to the control guide string.
const char *m_controlGuide{};
/// @brief The width of the panels. This is set according to the control guide text.
static inline int sm_panelWidth{};
/// @brief The menu used by all instances of BackupMenuState.
static inline std::shared_ptr<ui::Menu> sm_backupMenu{};
/// @brief The slide out panel used by all instances of BackupMenuState.
static inline std::shared_ptr<ui::SlideOutPanel> sm_slidePanel{};
/// @brief Inner render target so the menu only renders to a certain area.
static inline sdl::SharedTexture sm_menuRenderTarget{};
/// @brief Initializes the static members all instances share if they haven't been already.
void initialize_static_members();
/// @brief Checks for and tries to create the target directory if it hasn't been already.
void ensure_target_directory();
/// @brief Initializes the struct passed to tasks.
void initialize_task_data();
/// @brief Init's the string at the top of the backupmenu.
void initialize_info_string();
/// @brief Checks to see if the save data is empty.
void save_data_check();
/// @brief Ensures the remote storage is initalized and pointing to the right place.
void initialize_remote_storage();
/// @brief This is the function called when New Backup is selected.
void name_and_create_backup();
/// @brief This is the function called when a backup is selected to be overwritten.
void confirm_overwrite();
/// @brief This function is called to confirm restoring a backup.
void confirm_restore();
/// @brief Function called to confirm deleting a backup.
void confirm_delete();
/// @brief Uploads the currently selected backup to the remote storage.
void upload_backup();
/// @brief Just creates the pop-up that says Save is empty or w/e.
void pop_save_empty();
/// @brief Performs some operations and then marks the state for purging.
void deactivate_state();
inline bool user_is_system()
{
const uint8_t saveType = m_user->get_account_save_type();
return saveType == FsSaveDataType_System || saveType == FsSaveDataType_SystemBcat;
}
};

View File

@ -0,0 +1,55 @@
#pragma once
#include "sdl.hpp"
#include <memory>
class BaseState
{
public:
/// @brief Base application state class.
/// @param isClosable Optional. Controls whether or not the state should allow JKSV to close.
BaseState(bool isClosable = true);
/// @brief Ends homebutton and plus locking.
virtual ~BaseState();
/// @brief Every derived class is required to have this function.
virtual void update() = 0;
/// @brief Every derived class is required to have this function.
virtual void render() = 0;
/// @brief Deactivates state and allows JKSV to purge it from the vector.
void deactivate();
/// @brief Allows a state to be reactivated and pushed to the vector.
void reactivate();
/// @brief Returns if the state is still active.
/// @return Whether state is still active or can be purged.
bool is_active() const;
/// @brief Tells the state it's at the back of the vector and has focus.
void give_focus();
/// @brief Takes the focus away and tells the state it's no long back();
void take_focus();
/// @brief Allows the state to know whether it has focus.
/// @return Whether state has focus or not.
bool has_focus() const;
/// @brief Returns whether or not JKSV should allow closing while state is active.
/// @return True if closable. False if not.
bool is_closable() const;
private:
/// @brief Stores whether or not the state is currently active.
bool m_isActive = true;
/// @brief Stores whether or not the state has focus.
bool m_hasFocus{};
/// @brief Stores whether or not the state allows closing.
bool m_isClosable{};
};

View File

@ -0,0 +1,56 @@
#pragma once
#include "appstates/BaseState.hpp"
#include "sys/sys.hpp"
#include "ui/ColorMod.hpp"
#include <array>
#include <memory>
/// @brief Normally, I wouldn't do this, but this holds a single function both TaskState and ProgressState share...
class BaseTask : public BaseState
{
public:
/// @brief Constructor. Starts the glyph timer and sets AppState to not allow closing.
BaseTask();
/// @brief Virtual destructor.
virtual ~BaseTask() {};
/// @brief Runs the update routine for rendering the loading glyph animation.
/// @param
void update() override;
/// @brief Virtual render function.
virtual void render() = 0;
/// @brief This function renders the loading glyph in the bottom left corner.
/// @note This is mostly just so users don't think JKSV has frozen when operations take a long time.
void render_loading_glyph();
protected:
/// @brief Underlying system task. This needs to be allocated by the derived classes.
std::unique_ptr<sys::Task> m_task{};
/// @brief Updates the loading glyph animation.
void update_loading_glyph();
/// @brief This is the font size used for displaying text during tasks.
static inline constexpr int FONT_SIZE = 20;
private:
/// @brief This is the current frame of the loading glyph animation.
int m_currentFrame{};
/// @brief This is the timer used for changing the current glyph/frame of the animation.
sys::Timer m_frameTimer{};
/// @brief This is used to give the animation its pulsing color.
ui::ColorMod m_colorMod{};
/// @brief This is a pointer to the string popped when plus is pressed.
const char *m_popUnableExit{};
/// @brief This array holds the glyphs of the loading sequence. I think it's from the Wii?
static inline constexpr std::array<std::string_view, 8> sm_glyphArray =
{"\ue020", "\ue021", "\ue022", "\ue023", "\ue024", "\ue025", "\ue026", "\ue027"};
};

View File

@ -0,0 +1,62 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "ui/ui.hpp"
#include <memory>
#include <vector>
class BlacklistEditState final : public BaseState
{
public:
/// @brief Constructor. Loads the blacklist and ensures the panel is allocated.S
BlacklistEditState();
/// @brief Required destructor.
~BlacklistEditState() {};
/// @brief Creates and returns a new state.
static inline std::shared_ptr<BlacklistEditState> create() { return std::make_shared<BlacklistEditState>(); }
/// @brief Creates, pushes, then returns a new blacklist edit states.
static inline std::shared_ptr<BlacklistEditState> create_and_push()
{
auto newState = BlacklistEditState::create();
StateManager::push_state(newState);
return newState;
}
/// @brief Update override.
void update() override;
/// @brief Render override.
void render() override;
private:
/// @brief Local copy of the blacklist.
std::vector<uint64_t> m_blacklist{};
/// @brief Menu with blacklisted titles.
std::shared_ptr<ui::Menu> m_blacklistMenu{};
/// @brief This panel is shared by all instances.
static inline std::unique_ptr<ui::SlideOutPanel> sm_slidePanel{};
/// @brief Inits ^.
void initialize_static_members();
/// @brief Loads the blacklist from config.
void load_blacklist();
/// @brief Fills the menu with the titles blacklisted.
void initialize_menu();
/// @brief Updates the menu.
void refresh_menu();
/// @brief Removes the selected title from the blacklist and refreshes everything.
void remove_from_blacklist();
/// @brief Performs some operations and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,234 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/FadeState.hpp"
#include "appstates/ProgressState.hpp"
#include "appstates/TaskState.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "logging/logger.hpp"
#include "sdl.hpp"
#include "strings/strings.hpp"
#include "sys/sys.hpp"
#include "ui/ui.hpp"
#include <memory>
#include <string>
#include <switch.h>
namespace
{
// This is the base position on the screen used to center the Yes [A](...) text.
constexpr int COORD_YES_X = 460;
// ^ but for no.
constexpr int COORD_NO_X = 820;
} // namespace
/// @brief Templated class to create confirmation dialogs.
/// @tparam TaskType The type of task spawned on confirmation. Ex: Task, ProgressTask
/// @tparam StateType The state type spawned on confirmation. Ex: TaskState, ProgressState
/// @tparam StructType The type of struct passed to the state on confirmation.
template <typename TaskType, typename StateType, typename StructType>
class ConfirmState final : public BaseState
{
public:
/// @brief All functions passed to this state need to follow this signature: void function(<TaskType> *,
/// std::shared_ptr<<StructType>>)
using TaskFunction = void (*)(TaskType *, std::shared_ptr<StructType>);
/// @brief Constructor for new ConfirmState.
/// @param query The string displayed.
/// @param holdRequired Whether or not confirmation requires holding A for three seconds.
/// @param function Function executed on confirmation.
/// @param dataStruct shared_ptr<StructType> that is passed to function. I tried templating this and it was a nightmare.
ConfirmState(std::string_view query, bool holdRequired, TaskFunction function, std::shared_ptr<StructType> dataStruct)
: BaseState(false)
, m_query(query)
, m_yesText(strings::get_by_name(strings::names::YES_NO_OK, 0))
, m_noText(strings::get_by_name(strings::names::YES_NO_OK, 1))
, m_holdRequired(holdRequired)
, m_function(function)
, m_dataStruct(dataStruct)
{
ConfirmState::initialize_static_members();
ConfirmState::center_no();
ConfirmState::center_yes();
ConfirmState::load_holding_strings();
}
/// @brief Required even if it does nothing.
~ConfirmState() {};
/// @brief Returns a new ConfirmState. See constructor.
static inline std::shared_ptr<ConfirmState> create(std::string_view query,
bool holdRequired,
TaskFunction function,
std::shared_ptr<StructType> dataStruct)
{
return std::make_shared<ConfirmState>(query, holdRequired, function, dataStruct);
}
/// @brief Creates and returns a new ConfirmState and pushes it.
static inline std::shared_ptr<ConfirmState> create_and_push(std::string_view query,
bool holdRequired,
TaskFunction function,
std::shared_ptr<StructType> dataStruct)
{
// I'm gonna use a sneaky trick here. This shouldn't do this because it's confusing.
auto newState = create(query, holdRequired, function, dataStruct);
StateManager::push_state(newState);
return newState;
}
/// @brief Same as above but with a fade transition pushed in between.
static std::shared_ptr<ConfirmState> create_push_fade(std::string_view query,
bool holdRequired,
TaskFunction function,
std::shared_ptr<StructType> dataStruct)
{
auto newState = ConfirmState::create(query, holdRequired, function, dataStruct);
FadeState::create_and_push(colors::DIM_BACKGROUND, 0x00, 0x88, newState);
return newState;
}
/// @brief Just updates the ConfirmState.
void update() override
{
const bool aPressed = input::button_pressed(HidNpadButton_A);
const bool bPressed = input::button_pressed(HidNpadButton_B);
const bool aHeld = input::button_held(HidNpadButton_A);
const bool aReleased = input::button_released(HidNpadButton_A);
m_triggerGuard = m_triggerGuard || (aPressed && !m_triggerGuard);
const bool noHoldTrigger = m_triggerGuard && aPressed && !m_holdRequired;
const bool holdTriggered = m_triggerGuard && aPressed && m_holdRequired;
const bool holdSustained = m_triggerGuard && aHeld && m_holdRequired;
if (noHoldTrigger) { ConfirmState::confirmed(); }
else if (holdTriggered) { ConfirmState::hold_triggered(); }
else if (holdSustained) { ConfirmState::hold_sustained(); }
else if (aReleased) { ConfirmState::hold_released(); }
else if (bPressed) { ConfirmState::cancelled(); }
}
/// @brief Renders the state to screen.
void render() override
{
const bool hasFocus = BaseState::has_focus();
sdl::render_rect_fill(sdl::Texture::Null, 0, 0, 1280, 720, colors::DIM_BACKGROUND);
sm_dialog->render(sdl::Texture::Null, hasFocus);
sdl::text::render(sdl::Texture::Null, 312, 288, 20, 656, colors::WHITE, m_query);
sdl::render_line(sdl::Texture::Null, 280, 454, 999, 454, colors::DIV_COLOR);
sdl::render_line(sdl::Texture::Null, 640, 454, 640, 517, colors::DIV_COLOR);
sdl::text::render(sdl::Texture::Null, m_yesX, 476, 22, sdl::text::NO_WRAP, colors::WHITE, m_yesText);
sdl::text::render(sdl::Texture::Null, m_noX, 476, 22, sdl::text::NO_WRAP, colors::WHITE, m_noText);
}
private:
/// @brief This stores the query text.
std::string m_query{};
/// @brief These are pointers to the strings used to avoid call strings::get_by_name so much.
const char *m_yesText{}, *m_noText{};
/// @brief X coordinate to render the Yes No
int m_yesX{}, m_noX{};
/// @brief This is to prevent the dialog from triggering immediately.
bool m_triggerGuard{};
/// @brief Whether or not holding [A] to confirm is required.
const bool m_holdRequired{};
/// @brief These are pointers to the holding strings.
const char *m_holdText[3];
/// @brief Keep track of the ticks/time needed to confirm.
uint64_t m_startingTickCount{};
/// @brief Keeps track of which
int m_stage{};
/// @brief Function to execute if action is confirmed.
const TaskFunction m_function{};
/// @brief Pointer to data struct passed to ^
const std::shared_ptr<StructType> m_dataStruct{};
static inline std::shared_ptr<ui::DialogBox> sm_dialog{};
void initialize_static_members()
{
if (sm_dialog) { return; }
sm_dialog = ui::DialogBox::create(280, 262, 720, 256);
}
// This just centers the Yes or holding text.
void center_yes()
{
const int yesWidth = sdl::text::get_width(22, m_yesText);
m_yesX = COORD_YES_X - (yesWidth / 2);
}
void center_no()
{
const int noWidth = sdl::text::get_width(22, m_noText);
m_noX = COORD_NO_X - (noWidth / 2);
}
void load_holding_strings()
{
for (int i = 0; const char *string = strings::get_by_name(strings::names::HOLDING_STRINGS, i); i++)
{
m_holdText[i] = string;
}
}
void confirmed()
{
auto newState = std::make_shared<StateType>(m_function, m_dataStruct);
StateManager::push_state(newState);
BaseState::deactivate();
}
void hold_triggered()
{
m_startingTickCount = SDL_GetTicks64();
ConfirmState::change_holding_text(0);
}
void hold_sustained()
{
const uint64_t totalTicks = SDL_GetTicks64() - m_startingTickCount;
const bool threeSeconds = totalTicks >= 3000;
const bool twoSeconds = totalTicks >= 2000;
const bool oneSecond = totalTicks >= 1000;
if (threeSeconds) { ConfirmState::confirmed(); }
else if (twoSeconds) { ConfirmState::change_holding_text(2); }
else if (oneSecond) { ConfirmState::change_holding_text(1); }
}
void hold_released()
{
m_yesText = strings::get_by_name(strings::names::YES_NO_OK, 0);
ConfirmState::center_yes();
}
void cancelled()
{
FadeState::create_and_push(colors::DIM_BACKGROUND, 0x88, 0x00, nullptr);
BaseState::deactivate();
}
void change_holding_text(int index)
{
m_yesText = m_holdText[index];
ConfirmState::center_yes();
}
};

View File

@ -0,0 +1,76 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseTask.hpp"
#include "data/DataContext.hpp"
#include "sdl.hpp"
#include <functional>
#include <vector>
class DataLoadingState final : public BaseTask
{
public:
/// @brief This is a definition for functions that are called at destruction.
using DestructFunction = std::function<void()>;
template <typename... Args>
DataLoadingState(data::DataContext &context,
DestructFunction destructFunction,
void (*function)(sys::Task *, Args...),
Args... args)
: BaseTask()
, m_context(context)
, m_destructFunction(destructFunction)
{
DataLoadingState::initialize_static_members();
m_task = std::make_unique<sys::Task>(function, std::forward<Args>(args)...);
}
/// @brief Destructor. Runs all DestructFunctions in the vector.
~DataLoadingState() {};
template <typename... Args>
static inline std::shared_ptr<DataLoadingState> create(data::DataContext &context,
DestructFunction destructFunction,
void (*function)(sys::Task *, Args...),
Args... args)
{
return std::make_shared<DataLoadingState>(context, destructFunction, function, std::forward<Args>(args)...);
}
template <typename... Args>
static inline std::shared_ptr<DataLoadingState> create_and_push(data::DataContext &context,
DestructFunction destructFunction,
void (*function)(sys::Task *, Args...),
Args... args)
{
auto newState = DataLoadingState::create(context, destructFunction, function, std::forward<Args>(args)...);
StateManager::push_state(newState);
return newState;
}
/// @brief Update override.
void update() override;
/// @brief Render override.
void render() override;
private:
/// @brief Reference to the data context to run post-init operations.
data::DataContext &m_context;
/// @brief X coord of the status text.
int m_statusX{};
/// @brief The functions called upon destruction.
DestructFunction m_destructFunction{};
/// @brief Icon displayed in the center of the screen.
static inline sdl::SharedTexture sm_jksvIcon{};
/// @brief Loads the icon if it hasn't been already.
void initialize_static_members();
/// @brief
void deactivate_state();
};

View File

@ -0,0 +1,37 @@
#pragma once
#include "appstates/BaseState.hpp"
#include "sdl.hpp"
#include "ui/Menu.hpp"
/// @brief Extras menu.
class ExtrasMenuState final : public BaseState
{
public:
/// @brief Constructor.
ExtrasMenuState();
/// @brief Required even if nothing happens.
~ExtrasMenuState() {};
/// @brief Returns a new ExtrasMenuState
static inline std::shared_ptr<ExtrasMenuState> create() { return std::make_shared<ExtrasMenuState>(); }
/// @brief Updates the menu.
void update() override;
/// @brief Renders the menu to screen.
void render() override;
private:
/// @brief Menu
ui::Menu m_extrasMenu;
/// @brief Render target for menu.
sdl::SharedTexture m_renderTarget{};
/// @brief Creates and loads the menu strings.
void initialize_menu();
/// @brief This function is called when Reinitialize data is selected.
void reinitialize_data();
};

View File

@ -0,0 +1,84 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "sdl.hpp"
#include "sys/sys.hpp"
#include <memory>
class FadeState final : public BaseState
{
public:
/// @brief Direction (In/Out) of the fade. This is auto-determined at construction.
enum class Direction
{
In,
Out
};
/// @brief Creates a new fade in state.
/// @param nextState The next state to push after the the fade is finished.
FadeState(sdl::Color baseColor, uint8_t startAlpha, uint8_t endAlpha, std::shared_ptr<BaseState> nextState);
/// @brief Required destructor.
~FadeState() {};
/// @brief Returns a new fade in state. See constructor.
static inline std::shared_ptr<FadeState> create(sdl::Color baseColor,
uint8_t startAlpha,
uint8_t endAlpha,
std::shared_ptr<BaseState> nextState)
{
return std::make_shared<FadeState>(baseColor, startAlpha, endAlpha, nextState);
}
/// @brief Creates, returns and pushes a new FadeInState to the statemanager.
static std::shared_ptr<FadeState> create_and_push(sdl::Color baseColor,
uint8_t startAlpha,
uint8_t endAlpha,
std::shared_ptr<BaseState> nextState)
{
auto newState = FadeState::create(baseColor, startAlpha, endAlpha, nextState);
StateManager::push_state(newState);
return newState;
}
/// @brief Update override.
void update() override;
/// @brief Render override.
void render() override;
private:
sdl::Color m_baseColor{};
/// @brief Alpha value.
uint8_t m_alpha{};
/// @brief Alpha value to destruct at.
uint8_t m_endAlpha{};
/// @brief Direction (in/out) of the fade. This is auto determined according to alpha values passed.
FadeState::Direction m_direction{};
/// @brief The divisor found to make sure alpha ends evenly.
uint8_t m_divisor{};
/// @brief Timer for fade.
sys::Timer m_fadeTimer{};
/// @brief Pointer to the next state to push.
std::shared_ptr<BaseState> m_nextState{};
/// @brief Finds the highest divisor for the fade to use.
void find_divisor();
/// @brief Decreases alpha by m_divisor.
void decrease_alpha();
/// @brief Increases alpha by m_divisor.
void increase_alpha();
/// @brief Completes the fade.
void completed();
};

View File

@ -0,0 +1,109 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "data/data.hpp"
#include "sdl.hpp"
#include "ui/IconMenu.hpp"
#include <memory>
/// @brief The main
class MainMenuState final : public BaseState
{
public:
/// @brief Creates and initializes the main menu.
MainMenuState();
/// @brief Required even if it does nothing.
~MainMenuState() {};
/// @brief Returns a new MainMenuState
static inline std::shared_ptr<MainMenuState> create() { return std::make_shared<MainMenuState>(); }
/// @brief Creates and returns a new MainMenuState. Pushes it automatically.
static inline std::shared_ptr<MainMenuState> create_and_push()
{
auto newState = MainMenuState::create();
StateManager::push_state(newState);
return newState;
}
/// @brief Runs update routine.
void update() override;
/// @brief Renders menu to screen.
void render() override;
/// @brief Signals to
static void initialize_view_states();
/// @brief Calls refresh on on view states in the vector.
static void refresh_view_states();
// clang-format off
struct DataStruct
{
data::UserList userList;
};
// clang-format on
using TaskData = std::shared_ptr<MainMenuState::DataStruct>;
private:
/// @brief Render target this state renders to.
sdl::SharedTexture m_renderTarget{};
/// @brief The background gradient.
sdl::SharedTexture m_background{};
/// @brief Icon for the settings option,
sdl::SharedTexture m_settingsIcon{};
/// @brief Icon for the extras option.
sdl::SharedTexture m_extrasIcon{};
/// @brief Special menu type that uses icons.
std::shared_ptr<ui::IconMenu> m_mainMenu{};
/// @brief Pointer to control guide string so I don't need to call string::getByName every loop.
const char *m_controlGuide{};
/// @brief X coordinate of the control guide in the bottom right corner.
int m_controlGuideX{};
/// @brief This is the data struct passed to tasks.
MainMenuState::TaskData m_dataStruct{};
/// @brief Records the size of the sm_users vector.
static inline int sm_userCount{};
/// @brief This is the list of user pointers from data.
static inline data::UserList sm_users{};
/// @brief This is the pointer to the settings state.
static inline std::shared_ptr<BaseState> sm_settingsState{};
/// @brief This is the pointer to the extras state.
static inline std::shared_ptr<BaseState> sm_extrasState{};
/// @brief This is the vector of title selection states.
static inline std::vector<std::shared_ptr<BaseState>> sm_states{};
/// @brief Creates the settings and extras.
void initialize_settings_extras();
/// @brief Pushes the icons to the main menu.
void initialize_menu();
/// @brief Initializes the data struct.
void initialize_data_struct();
/// @brief Pushes the target state to the vector.
void push_target_state();
/// @brief Creates the user option state.
void create_user_options();
/// @brief Backups up all save data for all users.
void backup_all_for_all();
};

View File

@ -0,0 +1,70 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/FadeState.hpp"
#include "graphics/colors.hpp"
#include "ui/DialogBox.hpp"
#include <memory>
#include <string>
class MessageState final : public BaseState
{
public:
/// @brief Constructor.
/// @param message Message to display.
MessageState(std::string_view message);
/// @brief Required. Does NOTHING~
~MessageState() {};
/// @brief Creates and returns a new MessageState. See constructor.
static inline std::shared_ptr<MessageState> create(std::string_view message)
{
return std::make_shared<MessageState>(message);
}
/// @brief Same as above, only pushed to the StateManager before return.
static inline std::shared_ptr<MessageState> create_and_push(std::string_view message)
{
auto newState = MessageState::create(message);
StateManager::push_state(newState);
return newState;
}
/// @brief Same as above, but creates and pushes a transition fade in between.
static inline std::shared_ptr<MessageState> create_and_push_fade(std::string_view message)
{
auto newState = MessageState::create(message);
auto fadeState = FadeState::create_and_push(colors::DIM_BACKGROUND, 0x00, 0x88, newState);
return newState;
}
/// @brief Update override.
void update() override;
/// @brief Render override
void render() override;
private:
/// @brief Safe store of the message to display.
std::string m_message{};
/// @brief Prevents the state from auto triggering from a previous frame.
bool m_triggerGuard{};
/// @brief This is the OK string.
static inline const char *sm_okText{};
/// @brief The center X coord for the OK [A]
static inline int sm_okX{};
/// @brief All instances share this. There's no point in constantly allocating a new one.
static inline std::shared_ptr<ui::DialogBox> sm_dialog{};
/// @brief Allocates and ensures ^
void initialize_static_members();
/// @brief Pushes and empty fade in and marks the stage for purging.
void deactivate_state();
};

View File

@ -0,0 +1,75 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseTask.hpp"
#include "sys/sys.hpp"
#include "ui/ui.hpp"
#include <functional>
#include <memory>
#include <string>
#include <switch.h>
/// @brief State that shows progress of a task.
class ProgressState final : public BaseTask
{
public:
/// @brief Constructs a new ProgressState.
/// @param function Function for the task to run.
/// @param args Variadic arguments to be forwarded to the function passed.
/// @note All functions passed to this must follow this signature: void function(sys::ProgressTask *, <arguments>)
template <typename... Args>
ProgressState(void (*function)(sys::ProgressTask *, Args...), Args... args)
: BaseTask()
{
ProgressState::initialize_static_members();
m_task = std::make_unique<sys::ProgressTask>(function, std::forward<Args>(args)...);
}
/// @brief Required destructor.
~ProgressState() {};
/// @brief Creates and returns a new progress state.
template <typename... Args>
static inline std::shared_ptr<ProgressState> create(void (*function)(sys::ProgressTask *, Args...), Args... args)
{
return std::make_shared<ProgressState>(function, std::forward<Args>(args)...);
}
/// @brief Creates, pushes, then returns a new ProgressState.
template <typename... Args>
static inline std::shared_ptr<ProgressState> create_and_push(void (*function)(sys::ProgressTask *, Args...),
Args... args)
{
auto newState = ProgressState::create(function, std::forward<Args>(args)...);
StateManager::push_state(newState);
return newState;
}
/// @brief Checks if the thread is finished and deactivates this state.
void update() override;
/// @brief Renders the current progress to screen.
void render() override;
private:
/// @brief Progress which is saved as a rounded whole number.
size_t m_progress{};
/// @brief Width of the green bar in pixels.
size_t m_progressBarWidth{};
/// @brief X coordinate of the percentage string.
int m_percentageX{};
/// @brief Percentage as a string for printing to screen.
std::string m_percentageString{};
/// @brief This is the dialog box everything is rendered to.
static inline std::shared_ptr<ui::DialogBox> sm_dialog{};
/// @brief Initializes the shared dialog box.
void initialize_static_members();
/// @brief Performs some cleanup and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,79 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/TitleSelectCommon.hpp"
#include "data/data.hpp"
#include "ui/ui.hpp"
#include <atomic>
#include <memory>
/// @brief This is the state that is spawned when CreateSaveData is selected from the user menu.
class SaveCreateState final : public BaseState
{
public:
/// @brief Constructs a new SaveCreateState.
/// @param user The target user to create save data for.
/// @param titleSelect The selection view for the user for refreshing and rendering.
SaveCreateState(data::User *user, TitleSelectCommon *titleSelect);
/// @brief Required destructor.
~SaveCreateState() {};
/// @brief Returns a new SaveCreate state. See constructor for arguments.
static inline std::shared_ptr<SaveCreateState> create(data::User *user, TitleSelectCommon *titleSelect)
{
return std::make_shared<SaveCreateState>(user, titleSelect);
}
/// @brief Creates, pushes, returns and new SaveCreateState.
static inline std::shared_ptr<SaveCreateState> create_and_push(data::User *user, TitleSelectCommon *titleSelect)
{
auto newState = SaveCreateState::create(user, titleSelect);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs the update routine.
void update() override;
/// @brief Runs the render routine.
void render() override;
/// @brief This signals so data and the view can be refreshed on the next update() to avoid threading shenanigans.
void refresh_required();
private:
/// @brief Pointer to target user.
data::User *m_user{};
/// @brief Pointer to title selection view for the current user.
TitleSelectCommon *m_titleSelect{};
/// @brief Menu populated with every title found on the system.
std::shared_ptr<ui::Menu> m_saveMenu{};
/// @brief Vector of pointers to the title info. This allows sorting them alphabetically and other things.
std::vector<data::TitleInfo *> m_titleInfoVector{};
/// @brief Whether or not a refresh is required on the next update() call.
std::atomic<bool> m_refreshRequired{};
/// @brief Shared slide panel all instances use. There's no point in allocating a new one every time.
static inline std::unique_ptr<ui::SlideOutPanel> sm_slidePanel{};
/// @brief Initializes static members if they haven't been already.
void initialize_static_members();
/// @brief Retrieves the data needed from data::
void initialize_title_info_vector();
/// @brief Pushes the titles to the menu
void initialize_menu();
/// @brief Launches the save creation task.
void create_save_data_for();
/// @brief Performs some operations and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,83 @@
#pragma once
#include "appstates/BaseState.hpp"
#include "sdl.hpp"
#include "ui/Menu.hpp"
/// @brief The state for settings.
class SettingsState final : public BaseState
{
public:
/// @brief Constructs a new settings state.
SettingsState();
/// @brief Required destructor.
~SettingsState() {};
/// @brief Returns a new SettingsState.
static inline std::shared_ptr<SettingsState> create() { return std::make_shared<SettingsState>(); }
/// @brief Runs the update routine.
void update() override;
/// @brief Runs the render routine.
void render() override;
private:
/// @brief Menu for selecting and toggling settings.
std::shared_ptr<ui::Menu> m_settingsMenu{};
/// @brief Pointer to the control guide string.
const char *m_controlGuide{};
// These are pointers to strings this state uses constantly.
const char *m_onOff[2]{};
const char *m_sortTypes[3]{};
/// @brief X coordinate of the control guide in the bottom right corner.
int m_controlGuideX{};
/// @brief Render target to render to.
sdl::SharedTexture m_renderTarget{};
/// @brief Loads the settings menu strings.
void load_settings_menu();
/// @brief Loads the on/off and sort type strings.
void load_extra_strings();
/// @brief Runs a routine to update the menu strings for the menu.
void update_menu_options();
/// @brief Changes the current output directory of JKSV.
void change_working_directory();
/// @brief Creates and pushes the blacklist editing panel/state.
void create_push_blacklist_edit();
/// @brief Toggles or executes the code to changed the selected menu option.
void toggle_options();
/// @brief Creates and pushes the message with the description.
void create_push_description_message();
/// @brief Cycles and wraps the Zip compression level.
void cycle_zip_level();
/// @brief Cycles and wraps the title sort type.
void cycle_sort_type();
/// @brief Toggles JKSM mode and calls the main menu to reinitialize the views.
void toggle_jksm_mode();
/// @brief Toggles the trash folder and creates or deletes it if needed.
void toggle_trash_folder();
/// @brief Cycles and wraps the animation scaling (1.0 to 4.0);
void cycle_anim_scaling();
// Returns On/Off depending on the value passed.
const char *get_status_text(uint8_t value);
/// @brief Returns the sort type depending on the value passed.
const char *get_sort_type_text(uint8_t value);
};

View File

@ -0,0 +1,52 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseTask.hpp"
#include "sys/sys.hpp"
#include <switch.h>
/// @brief State that spawns a task and allows updates to be printed to screen.
class TaskState final : public BaseTask
{
public:
/// @brief Constructs and spawns a new TaskState.
/// @param function Function to run in the thread.
/// @param args Variadic templated arguments to forward.
/// @note All functions passed must follow this signature: void function(sys::Task *, <arguments>)
template <typename... Args>
TaskState(void (*function)(sys::Task *, Args...), Args... args)
: BaseTask()
{
m_task = std::make_unique<sys::Task>(function, std::forward<Args>(args)...);
}
/// @brief Required destructor.
~TaskState() {};
/// @brief Creates and returns a new TaskState.
template <typename... Args>
static inline std::shared_ptr<TaskState> create(void (*function)(sys::Task *, Args...), Args... args)
{
return std::make_shared<TaskState>(function, std::forward<Args>(args)...);
}
/// @brief Creates, pushes, then returns and new TaskState.
template <typename... Args>
static inline std::shared_ptr<TaskState> create_and_push(void (*function)(sys::Task *, Args...), Args... args)
{
auto newState = TaskState::create(function, std::forward<Args>(args)...);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs update routine. Waits for thread function to signal finish and deactivates.
void update() override;
/// @brief Run render routine. Prints m_task's status string to screen, basically.
/// @param
void render() override;
private:
/// @brief Performs some operations and marks the state for deletion.
void deactivate_state();
};

View File

@ -0,0 +1,60 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/TitleSelectCommon.hpp"
#include "data/data.hpp"
#include "sdl.hpp"
#include "ui/Menu.hpp"
/// @brief Text menu title selection state.
class TextTitleSelectState final : public TitleSelectCommon
{
public:
/// @brief Constructs new text menu title selection state.
/// @param user User to construct title select for.
TextTitleSelectState(data::User *user);
/// @brief Required destructor.
~TextTitleSelectState() {};
/// @brief Creates and returns a new TextTitleSelect. See constructor.
static inline std::shared_ptr<TextTitleSelectState> create(data::User *user)
{
return std::make_shared<TextTitleSelectState>(user);
}
/// @brief Creates, pushes, and returns a new TextTitleSelect.
static std::shared_ptr<TextTitleSelectState> create_and_push(data::User *user)
{
auto newState = TextTitleSelectState::create(user);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs update routine.
void update() override;
/// @brief Runs render routine.
void render() override;
/// @brief Refreshes view for changes.
void refresh() override;
private:
/// @brief Pointer to user view "belongs" to.
data::User *m_user{};
/// @brief Menu to display titles to select from.
std::shared_ptr<ui::Menu> m_titleSelectMenu{};
/// @brief Target to render to.
sdl::SharedTexture m_renderTarget{};
/// @brief Creates a new backup menu instance.
void create_backup_menu();
/// @brief Creates a new instance of the title options menu.
void create_title_option_menu();
/// @brief Adds or removes the current highlighted title to favorites.
void add_remove_favorite();
};

View File

@ -0,0 +1,70 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "data/data.hpp"
#include "sys/sys.hpp"
#include "ui/ui.hpp"
#include <memory>
#include <string>
#include <vector>
class TitleInfoState final : public BaseState
{
public:
/// @brief Constructs a new title info state.
/// @param user User to display info for.
/// @param titleInfo Title to display info for.
TitleInfoState(data::User *user, data::TitleInfo *titleInfo);
/// @brief Required destructor.
~TitleInfoState() {};
/// @brief Creates a new TitleInfoState.
static inline std::shared_ptr<TitleInfoState> create(data::User *user, data::TitleInfo *titleInfo)
{
return std::make_shared<TitleInfoState>(user, titleInfo);
}
/// @brief Creates, pushes, and returns a new TitleInfoState.
static inline std::shared_ptr<TitleInfoState> create_and_push(data::User *user, data::TitleInfo *titleInfo)
{
auto newState = TitleInfoState::create(user, titleInfo);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs update routine.
void update() override;
/// @brief Runs render routine.
void render() override;
private:
/// @brief Pointer to user.
data::User *m_user{};
/// @brief Pointer to title info.
data::TitleInfo *m_titleInfo{};
/// @brief This is a pointer to the title's icon.
sdl::SharedTexture m_icon{};
/// @brief This controls the clear color of the fields rendered.
bool m_fieldClear{};
/// @brief Slide panel.
static inline std::unique_ptr<ui::SlideOutPanel> sm_slidePanel{};
/// @brief Initializes the static members if they haven't been already.
void initialize_static_members();
/// @brief Creates the scrolling text/cheating fields.
void create_info_scrolls();
/// @brief Helper function for creating text fields.
std::shared_ptr<ui::TextScroll> create_new_scroll(std::string_view text, int y);
/// @brief Performs some operations and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,126 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/TitleSelectCommon.hpp"
#include "data/data.hpp"
#include "ui/ui.hpp"
#include <memory>
class TitleOptionState final : public BaseState
{
public:
/// @brief Constructs a new title option state.
/// @param user Target user.
/// @param titleInfo Target title.
TitleOptionState(data::User *user, data::TitleInfo *titleInfo, TitleSelectCommon *titleSelect);
/// @brief Required destructor.
~TitleOptionState() {};
/// @brief Returns a new TitleOptionState. See constructor.
static inline std::shared_ptr<TitleOptionState> create(data::User *user,
data::TitleInfo *titleInfo,
TitleSelectCommon *titleSelect)
{
return std::make_shared<TitleOptionState>(user, titleInfo, titleSelect);
}
/// @brief Creates, pushes, and returns a new TitleOptionState
static std::shared_ptr<TitleOptionState> create_and_push(data::User *user,
data::TitleInfo *titleInfo,
TitleSelectCommon *titleSelect)
{
auto newState = TitleOptionState::create(user, titleInfo, titleSelect);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs update routine.
void update() override;
/// @brief Runs the render routine.
void render() override;
/// @brief This function allows tasks to signal to the spawning state to close itself on the next update() call.
void close_on_update();
/// @brief Signals to the main thread that a view refresh is required on the next update() call.
void refresh_required();
// clang-format off
struct DataStruct
{
data::User *user{};
data::TitleInfo *titleInfo{};
TitleOptionState *spawningState{};
TitleSelectCommon *titleSelect{};
};
// clang-format on
using TaskData = std::shared_ptr<TitleOptionState::DataStruct>;
private:
/// @brief This is just in case the option should only apply to the current user.
data::User *m_user{};
/// @brief This is the target title.
data::TitleInfo *m_titleInfo{};
/// @brief Pointer to the title selection being used for updating.
TitleSelectCommon *m_titleSelect{};
/// @brief The struct passed to functions.
std::shared_ptr<TitleOptionState::DataStruct> m_dataStruct{};
/// @brief This holds whether or not the state should deactivate itself on the next update loop.
bool m_exitRequired{};
/// @brief This stores whether or a not a refresh is required on the next update().
bool m_refreshRequired{};
/// @brief Menu used and shared by all instances.
static inline std::shared_ptr<ui::Menu> sm_titleOptionMenu{};
/// @brief This is shared by all instances of this class.
static inline std::unique_ptr<ui::SlideOutPanel> sm_slidePanel{};
/// @brief Initializes the static members all instances share.
void initialize_static_members();
/// @brief Initializes the values of the struct passed to tasks.
void initialize_data_struct();
/// @brief Creates and pushes the title information state.
void create_push_info_state();
/// @brief Adds the currently highlighted title to the blacklist.
void add_to_blacklist();
/// @brief Changes the current output directory for the title locally and remotely (if needed).
void change_output_directory();
/// @brief Creates and pushes a new instance of file mode (once I get around to it.).
void create_push_file_mode();
/// @brief Deletes all locally stored save backups for the current title.
void delete_all_local_backups();
/// @brief Deletes all backups on the remote server.
void delete_all_remote_backups();
/// @brief Resets the save data for the current title. Basically wipes it clean.
void reset_save_data();
/// @brief Deletes the filesystem from the Switch.
void delete_save_from_system();
/// @brief Extends the container to any size desired.
void extend_save_container();
/// @brief Exports an SVI file for the current title.
void export_svi_file();
/// @brief Performs some operations and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,33 @@
#pragma once
#include "appstates/BaseState.hpp"
/// @brief Class that both view types are derived from.
class TitleSelectCommon : public BaseState
{
public:
/// @brief Constructs a new TitleSelectCommon. Basically just calculates the X coordinate of the control if it wasn't
/// already.
TitleSelectCommon();
/// @brief Required destructor.
virtual ~TitleSelectCommon() {};
/// @brief Required, inherited.
virtual void update() = 0;
/// @brief Required, inherited.
virtual void render() = 0;
/// @brief Both derived classes need this function.
virtual void refresh() = 0;
/// @brief Renders the control guide string to the bottom right corner.
void render_control_guide();
private:
/// @brief Pointer to the control guide string.
static inline const char *sm_controlGuide{};
/// @brief X coordinate the control guide is rendered at.
static inline int sm_controlGuideX{};
};

View File

@ -0,0 +1,66 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/TitleSelectCommon.hpp"
#include "data/data.hpp"
#include "sdl.hpp"
#include "ui/TitleView.hpp"
/// @brief Title select state with icon tiles.
class TitleSelectState final : public TitleSelectCommon
{
public:
/// @brief Constructs new title select state.
/// @param user User the state "belongs" to.
TitleSelectState(data::User *user);
/// @brief Required destructor.
~TitleSelectState() {};
/// @brief Returns a new TitleSelect state.
static inline std::shared_ptr<TitleSelectState> create(data::User *user)
{
return std::make_shared<TitleSelectState>(user);
}
/// @brief Creates, pushes, and returns a new TitleSelectState.
static inline std::shared_ptr<TitleSelectState> create_and_push(data::User *user)
{
auto newState = TitleSelectState::create(user);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs the update routine.
void update() override;
/// @brief Runs the render routine.
void render() override;
/// @brief Refreshes the view.
void refresh() override;
private:
/// @brief Pointer to the user the view belongs to.
data::User *m_user{};
/// @brief Target to render to.
sdl::SharedTexture m_renderTarget{};
/// @brief Tiled title selection view.
std::shared_ptr<ui::TitleView> m_titleView{};
/// @brief Checks if the user still has any games left to list. This is a safety measure.
bool title_count_check();
/// @brief Creates and pushes the backup menu state.
void create_backup_menu();
/// @brief Creates and pushes the title option state.
void create_title_option_menu();
/// @brief Resets and marks the state for purging.
void deactivate_state();
/// @brief Adds or removes the currently highlighted game from the favorite vector.
void add_remove_favorite();
};

View File

@ -0,0 +1,98 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/TitleSelectCommon.hpp"
#include "data/data.hpp"
#include "ui/ui.hpp"
#include <memory>
/// @brief State that allows certain actions to be taken for users.
class UserOptionState final : public BaseState
{
public:
/// @brief Constructs a new UserOptionState.
/// @param user Pointer to target user of the state.
/// @param titleSelect Pointer to the selection state for refresh and rendering.
UserOptionState(data::User *user, TitleSelectCommon *titleSelect);
/// @brief Required destructor.
~UserOptionState() {};
/// @brief Returns a new UserOptionState. See constructor.
static inline std::shared_ptr<UserOptionState> create(data::User *user, TitleSelectCommon *titleSelect)
{
return std::make_shared<UserOptionState>(user, titleSelect);
}
/// @brief Creates, pushes, and returns a new UserOptionState.
static inline std::shared_ptr<UserOptionState> create_and_push(data::User *user, TitleSelectCommon *titleSelect)
{
auto newState = UserOptionState::create(user, titleSelect);
StateManager::push_state(newState);
return newState;
}
/// @brief Runs the render routine.
void update() override;
/// @brief Runs the render routine.
void render() override;
/// @brief Signals to the main update() function that a refresh is needed.
/// @note Like this to prevent threading headaches.
void refresh_required();
// clang-format off
struct DataStruct
{
data::User *user{};
UserOptionState *spawningState{};
};
// clang-format on
using TaskData = std::shared_ptr<UserOptionState::DataStruct>;
private:
/// @brief Pointer to the target user.
data::User *m_user{};
/// @brief Pointer to the selection view.
TitleSelectCommon *m_titleSelect{};
/// @brief Menu that displays the options available.
std::shared_ptr<ui::Menu> m_userOptionMenu{};
/// @brief Shared pointer to pass data to tasks and functions.
std::shared_ptr<UserOptionState::DataStruct> m_dataStruct{};
/// @brief This allows spawned tasks to signal to the main thread to update the view.
bool m_refreshRequired{};
/// @brief Slide panel all instances shared.
static inline std::shared_ptr<ui::SlideOutPanel> sm_menuPanel{};
/// @brief Creates the panel if it hasn't been yet.
void create_menu_panel();
/// @brief Creates and pushes the strings needed for the menu
void load_menu_strings();
/// @brief Assigns the data within the struct to point where it needs to.
void initialize_data_struct();
/// @brief Starts the backup all operation.
void backup_all();
/// @brief Creates and pushes a new save creation menu.
void create_save_create();
/// @brief Launches the create all save data for use task.
void create_all_save_data();
/// @brief Deletes all save data for the user.
void delete_all_save_data();
/// @brief Performs operations to reset static members and marks the state for purging.
void deactivate_state();
};

View File

@ -0,0 +1,121 @@
#pragma once
#include "fslib.hpp"
#include <cstdint>
#include <json-c/json.h>
#include <map>
#include <string>
#include <vector>
namespace config
{
class ConfigContext
{
public:
ConfigContext() = default;
/// @brief Ensures the config directory exists.
void create_directory();
/// @brief Resets and saves the config back to the default values.
void reset();
/// @brief Loads the config and custom paths files from the SD card if possible.
bool load();
/// @brief Saves the config to the SD if possible.
void save();
/// @brief Attempts to find and return the value of the key passed.
uint8_t get_by_key(std::string_view key) const;
/// @brief Attempts to toggle the value for the key passed. For simple 1 and 0.
void toggle_by_key(std::string_view key);
/// @brief Attempts to set the key passed to the value passd.
void set_by_key(std::string_view key, uint8_t value);
/// @brief Returns the current working directory.
fslib::Path get_working_directory() const;
/// @brief Sets the current work directory if the path passed is valid.
bool set_working_directory(const fslib::Path &workDir);
/// @brief Returns the transition scaling speed.
double get_animation_scaling() const;
/// @brief Sets the current animation scaling speed.
void set_animation_scaling(double scaling);
/// @brief Adds a favorite to the favorite titles.
void add_favorite(uint64_t applicationID);
/// @brief Removes a title from the favorites.
void remove_favorite(uint64_t applicationID);
/// @brief Returns if the application ID passed is a favorite.
bool is_favorite(uint64_t applicationID) const;
/// @brief Adds the application ID passed to the blacklist.
void add_to_blacklist(uint64_t applicationID);
/// @brief Removes the application ID passed from the blacklist.
void remove_from_blacklist(uint64_t applicationID);
/// @brief Writes all of the currently blacklisted titles to the vector passed.
void get_blacklist(std::vector<uint64_t> &listOut);
/// @brief Returns if the application ID passed is found in the blacklist.
bool is_blacklisted(uint64_t applicationID) const;
/// @brief Returns if the blacklist is empty.
bool blacklist_empty() const;
/// @brief Returns if the application ID passed has a custom output path.
bool has_custom_path(uint64_t applicationID) const;
/// @brief Adds a new output path.
void add_custom_path(uint64_t applicationID, std::string_view newPath);
/// @brief Gets the custom output path of the applicationID passed.
void get_custom_path(uint64_t applicationID, char *buffer, size_t bufferSize);
private:
/// @brief This is where the majority of the config values are.
std::map<std::string, uint8_t, std::less<>> m_configMap{};
/// @brief The main directory JKSV operates from.
fslib::Path m_workingDirectory{};
/// @brief This scales the transitions.
double m_animationScaling{};
/// @brief This holds the favorite titles.
std::vector<uint64_t> m_favorites{};
/// @brief This holds the blacklisted titles.
std::vector<uint64_t> m_blacklist{};
/// @brief This holds user defined paths.
std::map<uint64_t, std::string> m_paths{};
/// @brief Loads the config file from SD.
bool load_config_file();
/// @brief Saves the config to the SD.
void save_config_file();
/// @brief Reads a json array to the vector passed.
void read_array_to_vector(std::vector<uint64_t> &vector, json_object *array);
/// @brief Loads the custom output path file from the SD if possible.
void load_custom_paths();
/// @brief Saves the custom output paths to the SD card.
void save_custom_paths();
/// @brief Searches the vector passed for the application ID passed.
std::vector<uint64_t>::const_iterator find_application_id(const std::vector<uint64_t> &vector,
uint64_t applicationID) const;
};
}

89
include/config/config.hpp Normal file
View File

@ -0,0 +1,89 @@
#pragma once
#include "config/keys.hpp"
#include "fslib.hpp"
#include <string_view>
#include <vector>
namespace config
{
/// @brief Attempts to load config from file. If it fails, loads defaults.
void initialize();
/// @brief Resets config to default values.
void reset_to_default();
/// @brief Saves config to file.
void save();
/// @brief Retrieves the config value according to the key passed.
/// @param key Key to retrieve. See config::keys
/// @return Key's value if found. 0 if it is not.
uint8_t get_by_key(std::string_view key);
/// @brief Toggles the key. This is only for basic true or false settings.
/// @param key Key to toggle.
void toggle_by_key(std::string_view key);
/// @brief Sets the key according
/// @param key Key to set.
/// @param value Value to set the key to.
void set_by_key(std::string_view key, uint8_t value);
/// @brief Returns the working directory.
/// @return Working directory.
fslib::Path get_working_directory();
/// @brief Attempts to set the working directory to the one passed.
/// @param path Path for JKSV to use.
bool set_working_directory(const fslib::Path &path);
/// @brief Returns the scaling speed of UI transitions and animations.
/// @return Scaling variable.
double get_animation_scaling();
/// @brief Sets the UI animation scaling.
/// @param newScale New value to set the scaling to.
void set_animation_scaling(double newScale);
/// @brief Adds or removes a title from the favorites list.
/// @param applicationID Application ID of title to add or remove.
void add_remove_favorite(uint64_t applicationID);
/// @brief Returns if the title is found in the favorites list.
/// @param applicationID Application ID to search for.
/// @return True if found. False if not.
bool is_favorite(uint64_t applicationID);
/// @brief Adds or removes title from blacklist.
/// @param applicationID Application ID to add or remove.
void add_remove_blacklist(uint64_t applicationID);
/// @brief Gets the currently blacklisted application IDs.
/// @param listOut Vector to store application IDs to.
void get_blacklisted_titles(std::vector<uint64_t> &listOut);
/// @brief Returns if the title is found in the blacklist.
/// @param applicationID Application ID to search for.
/// @return True if found. False if not.
bool is_blacklisted(uint64_t applicationID);
/// @brief Returns whether or not the blacklist is empty.
bool blacklist_is_empty();
/// @brief Adds a custom output path for the title.
/// @param applicationID Application ID of title to add a path for.
/// @param customPath Path to assign to the output.
void add_custom_path(uint64_t applicationID, std::string_view customPath);
/// @brief Searches to see if the application ID passed has a custom output path.
/// @param applicationID Application ID to check.
/// @return True if it does. False if it doesn't.
bool has_custom_path(uint64_t applicationID);
/// @brief Gets the custom, defined path for the title.
/// @param applicationID Application ID of title to get.
/// @param pathOut Buffer to write the path to.
/// @param pathOutSize Size of the buffer to write the path to.
void get_custom_path(uint64_t applicationID, char *pathOut, size_t pathOutSize);
} // namespace config

34
include/config/keys.hpp Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <string_view>
namespace config
{
namespace keys
{
inline constexpr std::string_view WORKING_DIRECTORY = "WorkingDirectory";
inline constexpr std::string_view INCLUDE_DEVICE_SAVES = "IncludeDeviceSaves";
inline constexpr std::string_view AUTO_BACKUP_ON_RESTORE = "AutoBackupOnRestore";
inline constexpr std::string_view AUTO_NAME_BACKUPS = "AutoNameBackups";
inline constexpr std::string_view AUTO_UPLOAD = "AutoUploadToRemote";
inline constexpr std::string_view USE_TITLE_IDS = "AlwaysUseTitleID";
inline constexpr std::string_view HOLD_FOR_DELETION = "HoldForDeletion";
inline constexpr std::string_view HOLD_FOR_RESTORATION = "HoldForRestoration";
inline constexpr std::string_view HOLD_FOR_OVERWRITE = "HoldForOverWrite";
inline constexpr std::string_view ONLY_LIST_MOUNTABLE = "OnlyListMountable";
inline constexpr std::string_view LIST_ACCOUNT_SYS_SAVES = "ListAccountSystemSaves";
inline constexpr std::string_view ALLOW_WRITING_TO_SYSTEM = "AllowSystemSaveWriting";
inline constexpr std::string_view EXPORT_TO_ZIP = "ExportToZip";
inline constexpr std::string_view ZIP_COMPRESSION_LEVEL = "ZipCompressionLevel";
inline constexpr std::string_view TITLE_SORT_TYPE = "TitleSortType";
inline constexpr std::string_view JKSM_TEXT_MODE = "JKSMTextMode";
inline constexpr std::string_view FORCE_ENGLISH = "ForceEnglish";
inline constexpr std::string_view SHOW_DEVICE_USER = "ShowDevice";
inline constexpr std::string_view SHOW_BCAT_USER = "ShowBCAT";
inline constexpr std::string_view SHOW_CACHE_USER = "ShowCache";
inline constexpr std::string_view SHOW_SYSTEM_USER = "ShowSystem";
inline constexpr std::string_view ENABLE_TRASH_BIN = "EnableTrash";
inline constexpr std::string_view UI_ANIMATION_SCALE = "UIAnimationScaling";
inline constexpr std::string_view FAVORITES = "Favorites";
inline constexpr std::string_view BLACKLIST = "BlackList";
} // namespace keys
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "fslib.hpp"
#include "sys/sys.hpp"
#include <condition_variable>
#include <mutex>
#include <vector>
namespace curl
{
// clang-format off
struct DownloadStruct
{
/// @brief Buffer mutex.
std::mutex lock{};
/// @brief Conditional for when the buffer is full.
std::condition_variable condition{};
/// @brief Shared buffer that is read into.
std::vector<sys::byte> sharedBuffer{};
/// @brief Bool to signal when the buffer is ready/empty.
bool bufferReady{};
/// @brief Destination file to write to.
fslib::File *dest{};
/// @brief Optional. Task to update with progress.
sys::ProgressTask *task{};
/// @brief Current offset in the file.
size_t offset{};
/// @brief Size of the file being downloaded.
int64_t fileSize{};
};
// clang-format on
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "fslib.hpp"
#include "sys/sys.hpp"
namespace curl
{
// clang-format off
struct UploadStruct
{
/// @brief Source file to upload from.
fslib::File *source{};
/// @brief Optional. Task to update with progress.
sys::ProgressTask *task{};
};
// clang-format on
}

149
include/curl/curl.hpp Normal file
View File

@ -0,0 +1,149 @@
#pragma once
#include "curl/DownloadStruct.hpp"
#include "curl/UploadStruct.hpp"
#include "fslib.hpp"
#include <curl/curl.h>
#include <memory>
#include <string>
#include <vector>
/// @brief This namespace contains various wrapped libcurl functions and data.
namespace curl
{
/// @brief JKSV's user agent string.
static constexpr const char *STRING_USER_AGENT = "JKSV";
/// @brief Self cleaning curl handle.
using Handle = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>;
/// @brief Self cleaning curl header/slist.
using HeaderList = std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)>;
/// @brief Definition for a vector containing headers received from libcurl.
using HeaderArray = std::vector<std::string>;
/// @brief Initializes lib curl.
/// @return True on success. False on failure.
bool initialize();
/// @brief Exits libcurl
void exit();
/// @brief Inline templated function to wrap curl_easy_setopt and make using curl::Handle slightly easier.
/// @tparam Option Templated type of the option. This is a headache so let the compiler figure it out.
/// @tparam Value Templated type of the value to set the option too. See above.
/// @param handle curl::Handle the option is being set for.
/// @param option CURLOPT to set.
/// @param value Value to set the CURLOPT to.
/// @return CURLcode returned from curl_easy_setopt.
template <typename Option, typename Value>
static inline CURLcode set_option(curl::Handle &handle, Option option, Value value)
{
return curl_easy_setopt(handle.get(), option, value);
}
/// @brief Inline function that returns a self cleaning curl handle.
/// @return Curl handle.
static inline curl::Handle new_handle() { return curl::Handle(curl_easy_init(), curl_easy_cleanup); }
/// @brief Inline function that returns a nullptr'd self cleaning curl_list.
/// @return Self cleaning curl_slist.
static inline curl::HeaderList new_header_list() { return curl::HeaderList(nullptr, curl_slist_free_all); }
/// @brief Inline wrapper function for curl_easy_reset.
/// @param curl curl::Handle to reset.
static inline void reset_handle(curl::Handle &curl)
{
curl_easy_reset(curl.get());
curl::set_option(curl, CURLOPT_USERAGENT, curl::STRING_USER_AGENT);
curl::set_option(curl, CURLOPT_CONNECTTIMEOUT, 5L);
}
/// @brief Logged inline wrapper function for curl_easy_perform.
/// @param handle Handle to perform.
bool perform(curl::Handle &handle);
/// @brief Inline wrapper function to make adding to HeaderList simpler.
/// @param headerList Header list to append to.
/// @param header Header to append.
void append_header(curl::HeaderList &list, std::string_view header);
/// @brief Curl callback function for reading data from a file.
/// @param buffer Incoming buffer from curl to read to.
/// @param size Element size.
/// @param count Element count.
/// @param target Target file to read from.
/// @return Number of bytes read so curl thinks everything went OK.
size_t read_data_from_file(char *buffer, size_t size, size_t count, curl::UploadStruct *upload);
/// @brief Curl callback function that writes incoming headers to a vector/array.
/// @param buffer Incoming buffer from curl.
/// @param size Element size.
/// @param count Element count.
/// @param array Array to write the header to.
/// @return size * count so curl thinks everything is fine, because it usually is.
size_t write_header_array(const char *buffer, size_t size, size_t count, curl::HeaderArray *array);
/// @brief Curl callback function that writes the response data to a C++ string.
/// @param buffer Incoming buffer from curl.
/// @param size Element size.
/// @param count Element count.
/// @param string String to write the response to.
/// @return size * count so curl thinks everything is fine.
size_t write_response_string(const char *buffer, size_t size, size_t count, std::string *string);
/// @brief Curl callback function that writes data directly to an fslib::File pointer.
/// @param buffer Incoming buffer from CURL.
/// @param size Element size.
/// @param count Element count.
/// @param target File to write the data to.
/// @return Number of bytes written to the file.
size_t write_data_to_file(const char *buffer, size_t size, size_t count, fslib::File *target);
/// @brief Threaded version of the above.
/// @param buffer Incoming data from curl
/// @param size size
/// @param count count
/// @param download Struct containing data for threaded download.
/// @return size * count
size_t download_file_threaded(const char *buffer, size_t size, size_t count, curl::DownloadStruct *download);
/// @brief Function used to download files threaded.
/// @param download Struct shared by both threads.
void download_write_thread_function(curl::DownloadStruct &download);
/// @brief Gets the value of a header from an array of headers.
/// @param array Array of headers to search.
/// @param header Header to search for.
/// @param valueOut String to write the value to.
bool get_header_value(const curl::HeaderArray &array, std::string_view header, std::string &valueOut);
/// @brief Gets the response code from the handle passed.
/// @param handle Handle to get the response code from.
long get_response_code(curl::Handle &handle);
/// @brief Calls curl_easy_escape using the passed curl::Handle.
/// @param handle Handle to use.
/// @param in String to escape.
/// @param out String to write the escaped text to.
bool escape_string(curl::Handle &handle, std::string_view in, std::string &out);
/// @brief Calls curl_easy_unescape using the passed curl::Handle.
/// @param handle Handle to use.
/// @param in String to unescape.
/// @param out String to write the escaped text to.
bool unescape_string(curl::Handle &handle, std::string_view in, std::string &out);
/// @brief Prepares the curl handle passed for a get request.
/// @param curl Handle to prepare for a get request.
void prepare_get(curl::Handle &curl);
/// @brief Prepares the curl handle for a post request.
/// @param curl Handle prepare for a post request.
void prepare_post(curl::Handle &curl);
/// @brief Prepares the curl handle for an upload.
/// @param curl Handle to reset and prepare for an upload.
void prepare_upload(curl::Handle &curl);
} // namespace curl

View File

@ -0,0 +1,25 @@
#pragma once
#include "sdl.hpp"
namespace data
{
class DataCommon
{
public:
/// @brief Default
DataCommon() = default;
/// @brief Function to load the icon to a texture.
virtual void load_icon() = 0;
/// @brief Returns the icon.
sdl::SharedTexture get_icon() { return m_icon; };
/// @brief Sets the icon.
void set_icon(sdl::SharedTexture &icon) { m_icon = icon; };
protected:
/// @brief Shared texture of the icon.
sdl::SharedTexture m_icon{};
};
}

View File

@ -0,0 +1,83 @@
#pragma once
#include "data/DataCommon.hpp"
#include "data/TitleInfo.hpp"
#include "data/User.hpp"
#include "sys/Task.hpp"
#include <mutex>
#include <unordered_map>
#include <vector>
namespace data
{
class DataContext
{
public:
/// @brief Default constructor.
DataContext() = default;
/// @brief Loads the users from the system and creates the accounts for system users.
bool load_create_users(sys::Task *task);
/// @brief Loops and runs the load routine for all users found.
void load_user_save_info(sys::Task *task);
/// @brief Gets a vector of pointers to the users loaded.
void get_users(data::UserList &listOut);
/// @brief Loads the titles from the Switch's application records.
void load_application_records(sys::Task *task);
/// @brief Returns whether a title is loaded with the application ID passed.
bool title_is_loaded(uint64_t applicationID);
/// @brief Attempts to load a title with the application ID passed.
void load_title(uint64_t applicationID);
/// @brief Returns the title info mapped to applicationID. nullptr on not found.
data::TitleInfo *get_title_by_id(uint64_t applicationID);
/// @brief Gets a vector of pointers to all of the current title info instances.
void get_title_info_list(data::TitleInfoList &listOut);
/// @brief Gets a list of title info that has savedata for type.
void get_title_info_list_by_type(FsSaveDataType type, data::TitleInfoList &listOut);
/// @brief Imports the SVI files from the SD card.
void import_svi_files(sys::Task *task);
/// @brief Deletes the cache file if it exists.
void delete_cache();
/// @brief Attempts to read the cache file from the SD card.
bool read_cache(sys::Task *task);
/// @brief Writes the cache to file.
bool write_cache(sys::Task *task);
/// @brief Processes the icon queue.
void process_icon_queue();
private:
/// @brief User vector.
std::vector<data::User> m_users{};
/// @brief Map of titles paired with their application ID.
std::unordered_map<uint64_t, data::TitleInfo> m_titleInfo{};
/// @brief Queue of the above to process the icons.
std::vector<data::DataCommon *> m_iconQueue{};
/// @brief Mutex for users.
std::mutex m_userMutex{};
/// @brief Mutex for titles.
std::mutex m_titleMutex{};
/// @brief Mutex to make sure the icon queue doesn't get mutilated.
std::mutex m_iconQueueMutex{};
/// @brief Whether or not the cache is still valid.
bool m_cacheIsValid{};
};
}

122
include/data/TitleInfo.hpp Normal file
View File

@ -0,0 +1,122 @@
#pragma once
#include "data/DataCommon.hpp"
#include "sdl.hpp"
#include <cstdint>
#include <memory>
#include <switch.h>
#include <vector>
namespace data
{
class TitleInfo;
/// @brief Vector of pointers to titleinfo instances.
using TitleInfoList = std::vector<data::TitleInfo *>;
/// @brief Class that holds data related to titles loaded from the system.
class TitleInfo final : public data::DataCommon
{
public:
/// @brief Constructs a TitleInfo instance. Loads control data, icon.
/// @param applicationID Application ID of title to load.
TitleInfo(uint64_t applicationID);
/// @brief Initializes a TitleInfo instance using external (cached) NsApplicationControlData
/// @param applicationID Application ID of the title loaded from cache.
/// @param controlData Reference to the control data to init from.
TitleInfo(uint64_t applicationID, std::unique_ptr<NsApplicationControlData> &controlData);
/// @brief Move constructor and operator.
TitleInfo(TitleInfo &&titleInfo);
TitleInfo &operator=(TitleInfo &&TitleInfo);
// None of this nonesense around these parts.TitleInfo(const TitleInfo &) = delete;
TitleInfo &operator=(const TitleInfo &) = delete;
/// @brief Returns the application ID of the title.
/// @return Title's application ID.
uint64_t get_application_id() const;
/// @brief Returns a pointer to the control data for the title.
/// @return Pointer to control data.
NsApplicationControlData *get_control_data();
/// @brief Returns whether or not the title has control data.
/// @return Whether or not the title has control data.
bool has_control_data() const;
/// @brief Returns the title of the title?
/// @return Title directly from the NACP.
const char *get_title();
/// @brief Returns the path safe version of the title for file system usage.
/// @return Path safe version of the title.
const char *get_path_safe_title() const;
/// @brief Returns the publisher of the title.
/// @return Publisher string from NACP.
const char *get_publisher();
/// @brief Returns the owner ID of the save data.
uint64_t get_save_data_owner_id() const;
/// @brief Returns the save data container's base size.
/// @param saveType Type of save data to return.
/// @return Size of baseline save data if applicable. If not, 0.
int64_t get_save_data_size(uint8_t saveType) const;
/// @brief Returns the maximum size of the save data container.
/// @param saveType Type of save data to return.
/// @return Maximum size of the save container if applicable. If not, 0.
int64_t get_save_data_size_max(uint8_t saveType) const;
/// @brief Returns the journaling size for the save type passed.
/// @param saveType Save type to return.
/// @return Journal size if applicable. If not, 0.
int64_t get_journal_size(uint8_t saveType) const;
/// @brief Returns the maximum journal size for the save type passed.
/// @param saveType Save type to return.
/// @return Maximum journal size if applicable. If not, 0.
int64_t get_journal_size_max(uint8_t saveType) const;
/// @brief Returns if a title uses the save type passed.
/// @param saveType Save type to check for.
/// @return True on success. False on failure.
bool has_save_data_type(uint8_t saveType) const;
/// @brief Returns a pointer to the icon texture.
/// @return Icon
sdl::SharedTexture get_icon() const;
/// @brief Allows the path safe title to be set to a new path.
/// @param newPathSafe Buffer containing the new safe path to use.
void set_path_safe_title(const char *newPathSafe);
/// @brief Loads the icon from the nacp.
void load_icon() override;
private:
/// @brief This defines how long the buffer is for the path safe version of the title.
static inline constexpr size_t SIZE_PATH_SAFE = 0x200;
/// @brief Stores application ID for easier grabbing since JKSV is all pointers.
uint64_t m_applicationID{};
/// @brief This contains the NACP and the icon.
std::unique_ptr<NsApplicationControlData> m_data{};
/// @brief Saves whether or not the title has control data.
bool m_hasData{};
/// @brief This is the path safe version of the title.
char m_pathSafeTitle[TitleInfo::SIZE_PATH_SAFE]{};
/// @brief Shared icon texture.
sdl::SharedTexture m_icon{};
/// @brief Private function to get/create the path safe title.
void get_create_path_safe_title();
};
} // namespace data

138
include/data/User.hpp Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include "data/DataCommon.hpp"
#include "fslib.hpp"
#include "sdl.hpp"
#include <string>
#include <string_view>
#include <switch.h>
#include <vector>
namespace data
{
class User;
/// @brief Type used to store save info and play statistics in the vector. Vector is used to preserve the order since I
/// can't use a map without having to extra heap allocate it.
using UserDataEntry = std::pair<uint64_t, std::pair<FsSaveDataInfo, PdmPlayStatistics>>;
/// @brief Type definition for the user save info/play stats vector.
using UserSaveInfoList = std::vector<UserDataEntry>;
/// @brief A vector of pointers to User instances.
using UserList = std::vector<data::User *>;
/// @brief Class that stores data for the user.
class User final : public data::DataCommon
{
public:
/// @brief Constructs a new user with accountID
/// @param accountID AccountID of user.
/// @param saveType Save data type account uses.
User(AccountUid accountID, FsSaveDataType saveType);
/// @brief This is the constructor used to create the fake system users.
/// @param accountID AccountID to associate with saveType.
/// @param pathSafeNickname The path safe version of the save data since JKSV is in everything the Switch supports.
/// @param iconPath Path to the icon to load for account.
/// @param saveType Save data type of user.
User(AccountUid accountID, std::string_view nickname, std::string_view pathSafeNickname, FsSaveDataType saveType);
/// @brief Move constructor and operator.
User(User &&user);
User &operator=(User &&user);
// Non of this around these parts.
User(const User &) = delete;
User &operator=(const User &) = delete;
/// @brief Pushes data to m_userData
/// @param saveInfo SaveDataInfo.
/// @param playStats Play statistics.
void add_data(const FsSaveDataInfo *saveInfo, const PdmPlayStatistics *playStats);
/// @brief Clears the user save info vector.
void clear_data_entries();
/// @brief Erases data at index.
/// @param index Index of save data info to erase.
void erase_data(int index);
/// @brief Runs the sort algo on the vector.
void sort_data();
/// @brief Returns the account ID of the user
AccountUid get_account_id() const;
/// @brief Returns the primary save data type o
FsSaveDataType get_account_save_type() const;
/// @brief Returns the user's full UTF-8 nickname.
const char *get_nickname() const;
/// @brief Returns the path safe version of the user's nickname.
const char *get_path_safe_nickname() const;
/// @brief Returns the total data entries.
size_t get_total_data_entries() const;
/// @brief Returns the application ID of the title at index.
uint64_t get_application_id_at(int index) const;
/// @brief Returns a pointer to the save data info at index.
FsSaveDataInfo *get_save_info_at(int index);
/// @brief Returns a pointer to the play statistics at index.
PdmPlayStatistics *get_play_stats_at(int index);
/// @brief Returns a pointer to the save info of applicationID.
FsSaveDataInfo *get_save_info_by_id(uint64_t applicationID);
/// @brief Returns a reference to the internal map for range based loops.
data::UserSaveInfoList &get_user_save_info_list();
/// @brief Returns a pointer to the play statistics of applicationID
PdmPlayStatistics *get_play_stats_by_id(uint64_t applicationID);
/// @brief Erases a UserDataEntry according to the application ID passed.
/// @param applicationID ID of the save to erase.
void erase_save_info_by_id(uint64_t applicationID);
/// @brief Loads the save data info and play statistics for the current user using the information passed to the
/// constructor.
void load_user_data();
/// @brief Loads the icon from the system and converts it to a texture.
void load_icon() override;
private:
/// @brief Account's ID
AccountUid m_accountID{};
/// @brief Type of save data account uses.
FsSaveDataType m_saveType{};
/// @brief User's nickname.
char m_nickname[0x20]{};
/// @brief Path safe version of nickname.
char m_pathSafeNickname[0x20]{};
/// @brief Vector containing save info and play statistics.
data::UserSaveInfoList m_userData{};
/// @brief Loads account structs from system.
/// @param profile AccountProfile struct to write to.
/// @param profileBase AccountProfileBase to write to.
void load_account(AccountProfile &profile, AccountProfileBase &profileBase);
/// @brief Creates a placeholder since something went wrong.
void create_account();
/// @brief Attempts to locate the data associated with applicationID
data::UserSaveInfoList::iterator find_title_by_id(uint64_t applicationID);
/// @brief Returns whether or not the index is within bounds.
inline bool index_check(int index) const { return index >= 0 && index < static_cast<int>(m_userData.size()); }
};
} // namespace data

View File

@ -0,0 +1,29 @@
#pragma once
#include <switch.h>
// This solves a lot of problems.
namespace data
{
static constexpr AccountUid BLANK_ACCOUNT_ID = {0};
} // namespace data
/// @brief Allows comparison of AccountUids since devkitpro decided a struct with two uint64_t's is better than u128
/// @param accountIDA First account to compare.
/// @param accountIDB Second account to compare.
/// @return True if both account IDs match.
static inline bool operator==(AccountUid accountIDA, AccountUid accountIDB)
{
return (accountIDA.uid[0] == accountIDB.uid[0]) && (accountIDA.uid[1] == accountIDB.uid[1]);
}
/// @brief Allows comparison of an AccountUid and a number.
/// @param accountIDA AccountUid to compare.
/// @param accountIDB Number to compare.
/// @return True if they match. False if they don't.
/// @note I'm not 100% sure which uint64_t in the AccountUid struct comes first. I don't know if it's [0][1] or [1][0]. To do:
/// Figure that out.
static inline bool operator==(AccountUid accountIDA, u128 accountIDB)
{
return accountIDA.uid[0] == (accountIDB >> 64 & 0xFFFFFFFFFFFFFFFF) &&
accountIDA.uid[1] == (accountIDB & 0xFFFFFFFFFFFFFFFF);
}

43
include/data/data.hpp Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "data/TitleInfo.hpp"
#include "data/User.hpp"
#include "data/accountUID.hpp"
#include "sys/sys.hpp"
#include <unordered_map>
#include <vector>
namespace data
{
/// @brief Launches the data loading/initialization state.
/// @param clear Whether or not the cache should be cleared.
/// @param onDestruction Function that is executed upon destruction of the data loading screen.
void launch_initialization(bool clear, std::function<void()> onDestruction);
/// @brief Writes pointers to users to vectorOut
/// @param userList List to push the pointers to.
void get_users(data::UserList &userList);
/// @brief Returns a pointer to the title mapped to applicationID.
/// @param applicationID ApplicationID of title to retrieve.
/// @return Pointer to data. nullptr if it's not found.
data::TitleInfo *get_title_info_by_id(uint64_t applicationID);
/// @brief Gets a vector of pointers to the title info.
/// @param listOut List to store pointers in.
void get_title_info_list(data::TitleInfoList &listOut);
/// @brief Uses the application ID passed to add/load a title to the map.
/// @param applicationID Application/System save data ID to add.
void load_title_to_map(uint64_t applicationID);
/// @brief Returns if the title with applicationID is already loaded to the map.
/// @param applicationID Application ID of the title to search for.
/// @return True if it has been. False if it hasn't.
bool title_exists_in_map(uint64_t applicationID);
/// @brief Gets a vector of pointers with all titles with saveType.
/// @param saveType Save data type to check for.
/// @param vectorOut Vector to push pointers to.
void get_title_info_by_type(FsSaveDataType saveType, data::TitleInfoList &listOut);
} // namespace data

66
include/fs/MiniUnzip.hpp Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "fslib.hpp"
#include <minizip/unzip.h>
#include <string_view>
namespace fs
{
class MiniUnzip
{
public:
MiniUnzip() = default;
/// @brief Constructor that c
MiniUnzip(const fslib::Path &path);
/// @brief Closes the unzFile.
~MiniUnzip();
/// @brief Returns whether or not the unzFile was successfully opened.
bool is_open() const;
/// @brief Attempts to open the path passed as a ZIP file.
bool open(const fslib::Path &path);
/// @brief Closes the unzfile.
void close();
/// @brief Attempts to go to the next file. Returns false at the end.
bool next_file();
/// @brief Closes the currently open file.
bool close_current_file();
/// @brief Attempts to locate a file with filename in the ZIP.
bool locate_file(std::string_view filename);
/// @brief Resets to the beginning file.
bool reset();
/// @brief Reads from the currently open file to the buffer passed.
ssize_t read(void *buffer, size_t bufferSize);
/// @brief Returns the name of the current file.
const char *get_filename();
/// @brief Returns the compressed size of the currently open file.
uint64_t get_compressed_size() const;
/// @brief Returns the uncompressed size of the the currently open file.
uint64_t get_uncompressed_size() const;
private:
/// @brief Underlying unzFile.
unzFile m_unz{};
/// @brief Saves whether or not opening the file was successful.
bool m_isOpen{};
/// @brief Info for the current file.
unz_file_info64 m_fileInfo{};
/// @brief Buffer containing the current file's name.
char m_filename[FS_MAX_PATH]{};
};
}

47
include/fs/MiniZip.hpp Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include "fslib.hpp"
#include <minizip/zip.h>
#include <string_view>
namespace fs
{
class MiniZip
{
public:
MiniZip() = default;
/// @brief Constructor. Calls open upon construction;
/// @param path
MiniZip(const fslib::Path &path);
/// @brief Closes the zipFile.
~MiniZip();
/// @brief Returns whether or not the zip file was successfully opened.
/// @return
bool is_open() const;
/// @brief Opens a Zip file at path
bool open(const fslib::Path &path);
/// @brief Manual call for closing the zipFile.
void close();
/// @brief Opens a new file with filename as the path.
bool open_new_file(std::string_view filename, bool trimPath = false, size_t trimPlaces = 0);
/// @brief Closes the currently open file in the Zip.
bool close_current_file();
/// @brief Attempts to write the buffer passed to the currently opened file.
bool write(const void *buffer, size_t dataSize);
private:
/// @brief Stores whether or not the zipFile was opened successfully.
bool m_isOpen{};
/// @brief Underlying ZIP file.
zipFile m_zip{};
};
}

25
include/fs/PathFilter.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "fslib.hpp"
#include <string>
#include <vector>
namespace fs
{
class PathFilter
{
public:
/// @brief Loads a path filter JSON file.
PathFilter(const fslib::Path &filterPath);
/// @brief Returns whether or not the filter has valid paths.
bool has_paths() const;
/// @brief Returns whether or not the path passed is filtered.
bool is_filtered(const fslib::Path &path);
private:
/// @brief Vector of paths to filter from deletion and backup.
std::vector<std::string> m_paths{};
};
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "data/TitleInfo.hpp"
#include <cstdint>
#include <switch.h>
namespace fs
{
/// @brief This is the magic value written to the beginning.
constexpr uint32_t SAVE_META_MAGIC = 0x56534B4A;
/// @brief This is the filename used for the save data meta info.
static constexpr std::string_view NAME_SAVE_META = ".nx_save_meta.bin";
// clang-format off
struct SaveMetaData
{
uint32_t magic{};
uint8_t revision{};
uint64_t applicationID{};
AccountUid accountID{};
uint64_t systemSaveID{};
uint8_t saveDataType{};
uint8_t saveDataRank{};
uint16_t saveDataIndex{};
uint64_t ownerID{};
uint64_t timestamp{};
uint32_t flags{};
int64_t saveDataSize{};
int64_t journalSize{};
uint64_t commitID{};
} __attribute__((packed));
// clang-format on
// I didn't want a separate file for this.
bool read_save_data_extra_info(const FsSaveDataInfo *saveInfo, FsSaveDataExtraData &dataOut);
/// @brief Didn't feel like a whole new file just for this. Fills an fs::SaveMetaData struct.
bool fill_save_meta_data(const FsSaveDataInfo *saveInfo, SaveMetaData &meta);
/// @brief Processes the save meta data and applies it to the passed saveInfo pointer.
/// @param saveInfo FsSaveDataInfo to apply the meta to.
/// @param meta Save meta data to apply.
bool process_save_meta_data(const FsSaveDataInfo *saveInfo, const SaveMetaData &meta);
} // namespace fs

View File

@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <switch.h>
namespace fs
{
class ScopedSaveMount
{
public:
/// @brief Opens a scope save mount using the FsSaveDataInfo passed.
/// @param mount Mount point.
/// @param info Save info to mount.
/// @param log Optional. Whether or not logging errors is wanted. True by default.
ScopedSaveMount(std::string_view mount, const FsSaveDataInfo *saveInfo, bool log = true);
ScopedSaveMount(ScopedSaveMount &&scopedSaveMount);
ScopedSaveMount &operator=(ScopedSaveMount &&scopedSaveMount);
ScopedSaveMount(const ScopedSaveMount &) = delete;
ScopedSaveMount &operator=(const ScopedSaveMount &) = delete;
/// @brief Closes the save mounted.
~ScopedSaveMount();
/// @brief Returns whether or not mounting the data was successful.
bool is_open() const;
private:
/// @brief Saves a copy of the mount point for destruction.
std::string m_mountPoint{};
/// @brief Stores whether or not mounting the save was successful.
bool m_isOpen{};
bool m_log{};
};
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "fslib.hpp"
namespace fs
{
/// @brief Retrieves the total size of the contents of the directory at targetPath.
/// @param targetPath Directory to calculate.
uint64_t get_directory_total_size(const fslib::Path &targetPath);
/// @brief Checks if directory is empty. Didn't feel like this needs its own source file.
/// @param directoryPath Path to directory to check.
/// @return True if directory has files inside.
bool directory_has_contents(const fslib::Path &directoryPath);
} // namespace fs

10
include/fs/fs.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "fs/MiniUnzip.hpp"
#include "fs/MiniZip.hpp"
#include "fs/SaveMetaData.hpp"
#include "fs/ScopedSaveMount.hpp"
#include "fs/directory_functions.hpp"
#include "fs/io.hpp"
#include "fs/save_data_functions.hpp"
#include "fs/save_mount.hpp"
#include "fs/zip.hpp"

43
include/fs/io.hpp Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include "fslib.hpp"
#include "sys/sys.hpp"
#include <string_view>
namespace fs
{
/// @brief Copies source to destination.
/// @param source Path to source file.
/// @param destination Path to destination.
/// @param journalSize Optional. The size of the journal if data needs to be commited.
/// @param commitDevice Optional. The device to commit to if it's needed.
/// @param Task Optional. Progress tracking task to display progress of operation if needed.
void copy_file(const fslib::Path &source, const fslib::Path &destination, sys::ProgressTask *Task = nullptr);
/// @brief Same as a above. Committing data to the device passed while copying.
/// @param device Device to commit data to.
/// @param journalSize Size of the journal area of the save data.
void copy_file_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task = nullptr);
/// @brief Recursively copies source to destination.
/// @param source Source path.
/// @param destination Destination path.
/// @param journalSize Optional. Journal size to be passed to copyFile if data needs to be commited to device.
/// @param commitDevice Optional. Device to commit data to if needed.
/// @param Task Option. Progress tracking task to be passed to copyFile to show progress of operation.
void copy_directory(const fslib::Path &source, const fslib::Path &destination, sys::ProgressTask *Task = nullptr);
/// @brief Same as above but committing data passed while copying.
/// @param device Device to commit data to.
/// @param journalSize Size of the journaling area of the save.
void copy_directory_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task = nullptr);
} // namespace fs

View File

@ -0,0 +1,30 @@
#pragma once
#include "data/data.hpp"
#include <switch.h>
namespace fs
{
/// @brief Creates save data for the target user for the title passed.
/// @param targetUser User to create save data for.
/// @param titleInfo Title to create save data for.
/// @return True on success. False on failure.
bool create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo);
/// @brief Deletes the save data of the FsSaveDataInfo passed.
/// @param saveInfo Save data to delete.
/// @return True on success. False on failure.
bool delete_save_data(const FsSaveDataInfo *saveInfo);
/// @brief Extends the save data of the FsSaveDataInfo struct passed.
/// @param saveInfo Pointer to the FsSaveDataInfo struct of the save to extend.
/// @param size Size (in MB) to extend the save data to.
/// @param journalSize Size of the journaling space.
/// @return True on success. False on failure.
bool extend_save_data(const FsSaveDataInfo *saveInfo, int64_t size, int64_t journalSize);
/// @brief Returns whether or not the saveInfo passed is system type.
/// @param saveInfo FsSaveDataInfo to check.
/// @return True if it is. False if it isn't.
/// @note The config setting overrides this.
bool is_system_save_data(const FsSaveDataInfo *saveInfo);
} // namespace fs

12
include/fs/save_mount.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <string_view>
#include <switch.h>
namespace fs
{
/// @brief Default mount point used for JKSV for saves.
static constexpr std::string_view DEFAULT_SAVE_MOUNT = "save";
/// @brief Same as above, but as a root directory.
static constexpr std::string_view DEFAULT_SAVE_ROOT = "save:/";
} // namespace fs

26
include/fs/zip.hpp Normal file
View File

@ -0,0 +1,26 @@
#pragma once
// Major to do: Stop using minizip and finish the ZipFile class.
#include "fs/MiniUnzip.hpp"
#include "fs/MiniZip.hpp"
#include "fs/fs.hpp"
#include "fslib.hpp"
#include <string_view>
namespace fs
{
/// @brief Copies source to destination.
/// @note Task is optional.
void copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys::ProgressTask *Task = nullptr);
/// @brief Unzips source to destination.
/// @note Task is optional.
void copy_zip_to_directory(fs::MiniUnzip &source,
const fslib::Path &dest,
int64_t journalSize,
std::string_view commitDevice,
sys::ProgressTask *Task = nullptr);
/// @brief Returns whether or not zip has files inside besides the save meta.
bool zip_has_contents(const fslib::Path &zipPath);
} // namespace fs

View File

@ -0,0 +1,26 @@
#pragma once
#include "sdl.hpp"
namespace colors
{
inline constexpr sdl::Color WHITE = {0xFFFFFFFF};
inline constexpr sdl::Color BLACK = {0x000000FF};
inline constexpr sdl::Color RED = {0xFF0000FF};
inline constexpr sdl::Color DARK_RED = {0xDD0000FF};
inline constexpr sdl::Color GREEN = {0x00FF00FF};
inline constexpr sdl::Color BLUE = {0x0099EEFF};
inline constexpr sdl::Color YELLOW = {0xF8FC00FF};
inline constexpr sdl::Color PINK = {0xFF4444FF};
inline constexpr sdl::Color BLUE_GREEN = {0x00FFC5FF};
inline constexpr sdl::Color CLEAR_COLOR = {0x2D2D2DFF};
inline constexpr sdl::Color DIALOG_DARK = {0x505050FF};
inline constexpr sdl::Color DIALOG_LIGHT = {0xDCDCDCFF};
inline constexpr sdl::Color DIM_BACKGROUND = {0x00000088};
inline constexpr sdl::Color TRANSPARENT = {0x00000000};
inline constexpr sdl::Color SLIDE_PANEL_CLEAR = {0x000000CC};
inline constexpr sdl::Color DIV_COLOR = {0x707070FF};
inline constexpr uint8_t ALPHA_FADE_BEGIN = 0x00;
inline constexpr uint8_t ALPHA_FADE_END = 0x88;
} // namespace colors

View File

@ -0,0 +1,16 @@
#pragma once
#include "sdl.hpp"
#include <string_view>
// This file contains basic graphics functions various parts of JKSV use.
namespace gfxutil
{
/// @brief Generates a generic icon for saves and titles that lack one.
/// @param text Text to be centered and rendered to the icon.
/// @param fontSize Size of the font to use.
/// @param background Background color to use.
/// @param foreground Color to use to render the text.
/// @return sdl::SharedTexture of the icon.
sdl::SharedTexture create_generic_icon(std::string_view text, int fontSize, sdl::Color background, sdl::Color foreground);
} // namespace gfxutil

23
include/input.hpp Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <switch.h>
namespace input
{
/// @brief Initializes PadState and input.
void initialize();
/// @brief Updates the PadState.
void update();
/// @brief Returns if a button was pressed the current frame, but not the previous.
/// @param button Button to check.
bool button_pressed(HidNpadButton button);
/// @brief Returns if the button was pressed or held the previous and current frame.
/// @param button Button to check.
bool button_held(HidNpadButton button);
/// @brief Returns if the button was pressed or held the previous frame, but not the current.
/// @param button Button to check.
bool button_released(HidNpadButton button);
} // namespace input

19
include/keyboard.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <string_view>
#include <switch.h>
namespace keyboard
{
/// @brief Gets input using the Switch's keyboard.
/// @param keyboardType Type of keyboard shown.
/// @param defaultText The default text in the keyboard.
/// @param header The header of the keyboard.
/// @param stringOut Pointer to buffer to write to.
/// @param stringLength Size of the buffer to write too.
/// @return True if input was successful and valid. False if it wasn't.
bool get_input(SwkbdType keyboardType,
std::string_view defaultText,
std::string_view header,
char *stringOut,
size_t stringLength);
} // namespace keyboard

15
include/logging/error.hpp Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <source_location>
#include <switch.h>
namespace error
{
/// @brief Logs and returns if a call from libnx fails.
bool libnx(Result code, const std::source_location &location = std::source_location::current());
/// @brief Logs and returns if an fslib function fails.
bool fslib(bool result, const std::source_location &location = std::source_location::current());
/// @brief Returns whether or not the pointer passed is null. Records the location in which this occurred.
bool is_null(const void *pointer, const std::source_location &location = std::source_location::current());
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <string_view>
namespace logger
{
/// @brief Creates the log file if it doesn't exist already.
void initialize();
/// @brief Logs a formatted string.
/// @param format Format of string.
/// @param arguments Va arguments.
void log(const char *format, ...);
} // namespace logger

11
include/mathutil.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
namespace math
{
template <typename Type>
class Util
{
public:
static inline Type absolute_distance(Type a, Type b) { return a > b ? a - b : b - a; }
};
}

43
include/remote/Form.hpp Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#include <string>
namespace remote
{
/// @brief This is a class to build URL encoded form bodies and be more readable than snprintfs.
class Form
{
public:
Form() = default;
/// @brief Copy constructor
/// @param form Form to copy from.
Form(const Form &form);
/// @brief Move constructor.
/// @param form Form to copy from.
Form(Form &&form);
/// @brief = Operator.
/// @param form Form to copy.
Form &operator=(const Form &form);
/// @brief = Move operator.
/// @param form Form to rob of its life.
Form &operator=(Form &&form);
/// @brief Appends a parameter to the form/URL encoded text.
/// @param param Parameter to append.
/// @param value Value to append.
Form &append_parameter(std::string_view param, std::string_view value);
/// @brief Returns the C string of the form string.
const char *get() const;
/// @brief Returns m_form.length()
size_t length() const;
private:
/// @brief String containing the actual data posted.
std::string m_form{};
};
} // namespace remote

View File

@ -0,0 +1,100 @@
#pragma once
#include "JSON.hpp"
#include "remote/Storage.hpp"
#include <ctime>
namespace remote
{
class GoogleDrive final : public remote::Storage
{
public:
/// @brief Loads the config from SD.
GoogleDrive();
/// @brief Creates a directory on Google Drive.
/// @param name Name of the directory to create.
bool create_directory(std::string_view name) override;
/// @brief Uploads the file from source. File name is used to name the file.
/// @param source Path to upload the file from.
bool upload_file(const fslib::Path &source, std::string_view name, sys::ProgressTask *task = nullptr) override;
/// @brief Patches or updates the file on Google Drive.
/// @param file Pointer to the item containing the data needed to update the file.
/// @param source Source path to update from.
bool patch_file(remote::Item *file, const fslib::Path &source, sys::ProgressTask *task = nullptr) override;
/// @brief Downloads a file from Google Drive.
/// @param file Pointer to the item containing data to download the file.
/// @param destination Location to write the downloaded file to.
bool download_file(const remote::Item *file,
const fslib::Path &destination,
sys::ProgressTask *task = nullptr) override;
/// @brief Deletes an item from Google Drive.
/// @param item Pointer to item containing data to delete the item.
bool delete_item(const remote::Item *item) override;
/// @brief Renames an item on Google Drive.
/// @param item Item to rename.
/// @param newName New name of the item.
bool rename_item(remote::Item *item, std::string_view newName) override;
/// @brief Returns whether or not a sign in is required to use drive. AKA the refresh token is missing.
bool sign_in_required() const;
/// @brief Requests the the necessary data from Google to login.
/// @param message String to store the message to.
/// @param code String to store the device code from Google.
/// @param expiration time_t to store when the sign in window closes.
/// @param wait Int to store the time in seconds between server pings.
bool get_sign_in_data(std::string &message, std::string &code, std::time_t &expiration, int &wait);
/// @brief This is the function that pings the server to see if the user entered the code yet.
/// @param code The code Google reponded with for verification.
/// @return If the user signs in, true. If not, false;
bool poll_sign_in(std::string_view code);
private:
/// @brief Google client ID.
std::string m_clientId{};
/// @brief Google client secret.
std::string m_clientSecret{};
/// @brief Authentication token.
std::string m_token{};
/// @brief Token used for refreshing token when it expires.
std::string m_refreshToken{};
/// @brief This is to save the authentication header string instead of recreating it over and over.
std::string m_authHeader{};
/// @brief This is the calculate time when the auth token expires.
std::time_t m_tokenExpires{};
/// @brief Uses V2 of Drive's API to get the root directory ID from Google.
bool get_root_id();
/// @brief Returns whether or not the auth token is still valid for use or needs to be refreshed.
bool token_is_valid() const;
/// @brief Attempts to refresh the auth token if needed.
bool refresh_token();
/// @brief Requests and processes the entire listing for JKSV.
bool request_listing();
/// @brief Processes and listing
/// @param json Json object to use for parsing.
bool process_listing(json::Object &json);
/// @brief Performs a quick check on the json object passed for errors.
/// @param json Json object to check.
/// @param log Whether or not to log the error.
/// @note This doesn't catch every error. Google's errors aren't consistent.
bool error_occurred(json::Object &json, bool log = true);
};
} // namespace remote

73
include/remote/Item.hpp Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include <string>
namespace remote
{
class Item
{
public:
/// @brief Remote item constructor.
/// @param name Item's name.
/// @param id Item's ID.
/// @param parent Item's parent.
/// @param size Size of the time.
/// @param directory Whether or not the item is a directory.
Item(std::string_view name, std::string_view id, std::string_view parent, size_t size, bool directory);
/// @brief Returns the name of the item.
/// @return Name of the item.
std::string_view get_name() const;
/// @brief Returns the id of the item.
/// @return ID of the item.
std::string_view get_id() const;
/// @brief Returns the parent id of the item.
/// @return Parent ID of the item.
std::string_view get_parent_id() const;
/// @brief Gets the size of the item.
/// @return Size of the item in bytes.
size_t get_size() const;
/// @brief Returns whether or not the item is a directory.
/// @return Whether or not the item is a directory.
bool is_directory() const;
/// @brief Sets the name of the item.
/// @param name New name of the item.
void set_name(std::string_view name);
/// @brief Sets the ID of the item.
/// @param id New ID of the item.
void set_id(std::string_view id);
/// @brief Sets the parent ID of the item.
/// @param parent Parent ID of the item.
void set_parent_id(std::string_view parent);
/// @brief Sets the size of the item.
/// @param size Size of the item.
void set_size(size_t size);
/// @brief Sets whether or not the item is a directory.
/// @param directory Whether or not the item is a directory.
void set_is_directory(bool directory);
private:
/// @brief The name of the item.
std::string m_name;
/// @brief The ID of the item.
std::string m_id;
/// @brief Parent ID of the item.
std::string m_parent;
/// @brief Size of the item.
size_t m_size;
/// @brief Whether or not the item is a directory.
bool m_isDirectory;
};
} // namespace remote

157
include/remote/Storage.hpp Normal file
View File

@ -0,0 +1,157 @@
#pragma once
#include "curl/curl.hpp"
#include "fslib.hpp"
#include "remote/Item.hpp"
#include "sys/sys.hpp"
#include <ctime>
#include <string>
#include <vector>
namespace remote
{
class Storage
{
public:
/// @brief Definition to make things easier to type.
using DirectoryListing = std::vector<remote::Item *>;
/// @brief This makes writing some stuff for these classes way easier.
using List = std::vector<remote::Item>;
/// @brief This just allocates the curl::Handle. Never mind.
Storage(std::string_view prefix, bool supportsUtf8 = false);
/// @brief Returns whether or not the Storage type was successfully. initialized.
bool is_initialized() const;
// Directory functions.
/// @brief Returns whether or not a directory with name exists within the current parent.
/// @param name Name of the directory to search for.
bool directory_exists(std::string_view name);
/// @brief Returns the parent to the root directory.
void return_to_root();
/// @brief This allows the root to be set to something other than what it originally was at construction.
/// @param root Item to be used as the new root.
void set_root_directory(const remote::Item *root);
/// @brief Changes the current parent directory.
/// @param Item Item to use as the current parent directory.
void change_directory(const remote::Item *item);
/// @brief Creates a directory in the current parent directory.
/// @param name Name of the directory to create.
virtual bool create_directory(std::string_view name) = 0;
/// @brief Searches the list for a directory matching name and the current parent.
/// @param name Name of the directory to search for.
/// @return Pointer to the item representing the directory on success. nullptr on failure/not found.
remote::Item *get_directory_by_name(std::string_view name);
/// @brief Retrieves a listing of the items in the current parent directory.
/// @param listOut List to fill.
void get_directory_listing(Storage::DirectoryListing &listOut);
/// @brief Same as above, but allows any parent to be used.
/// @param item Item that is to be treated as a parent.
/// @param listOut List to fill.
void get_directory_listing_with_parent(const remote::Item *item, Storage::DirectoryListing &listOut);
// File functions.
/// @brief Returns whether a file with name exists within the current directory.
/// @param name Name of the file.
bool file_exists(std::string_view name);
/// @brief Uploads a file from the SD card to the remote.
/// @param source Path to the file to upload.
virtual bool upload_file(const fslib::Path &source, std::string_view name, sys::ProgressTask *task = nullptr) = 0;
/// @brief Patches or updates a file on the remote.
/// @param item Item to be updated.
/// @param source Path to the file to update with.
virtual bool patch_file(remote::Item *file, const fslib::Path &source, sys::ProgressTask *task = nullptr) = 0;
/// @brief Downloads a file from the remote.
/// @param item Item to download.
/// @param destination Path to download the file to.
virtual bool download_file(const remote::Item *file,
const fslib::Path &destination,
sys::ProgressTask *task = nullptr) = 0;
/// @brief Searches the list for a file matching name and the current parent.
/// @param name Name of the file to search for.
/// @return Pointer to the item if located. nullptr if not.
remote::Item *get_file_by_name(std::string_view name);
// General functions that apply to both.
/// @brief Deletes a file or folder from the remote.
/// @param item Item to delete.
virtual bool delete_item(const remote::Item *item) = 0;
/// @brief Renames a file on the remote server.
/// @param item Target item to rename.
/// @param newName New name of the target item.
virtual bool rename_item(remote::Item *item, std::string_view newName) = 0;
/// @brief Returns whether or not the remote storage type supports UTF-8 for names or requires path safe titles.
bool supports_utf8() const;
/// @brief Returns the prefix for menus.
std::string_view get_prefix() const;
protected:
/// @brief This is the size of the buffers used for snprintf'ing URLs together.
static constexpr size_t SIZE_URL_BUFFER = 0x401;
/// @brief This is the size used for uploads.
static constexpr size_t SIZE_UPLOAD_BUFFER = 0x10000;
/// @brief Curl handle.
curl::Handle m_curl;
/// @brief This allows JKSV to know whether or not the storage type supports UTF-8.
bool m_utf8Paths{};
/// @brief This is the prefix used for menus.
std::string m_prefix{};
/// @brief This stores whether or not the instance was initialized successfully.
bool m_isInitialized{};
/// @brief This is the root directory of the remote storage.
std::string m_root{};
/// @brief This stores the current parent.
std::string m_parent{};
/// @brief This is the main remote listing.
Storage::List m_list{};
/// @brief Searches the list for a directory matching name and the current parent.
/// @param name Name to search for.
Storage::List::iterator find_directory_by_name(std::string_view name);
/// @brief Searches the list for a directory matching ID.
/// @param id ID of the directory to search for.
Storage::List::iterator find_directory_by_id(std::string_view id);
/// @brief Searches to find if a file with name exists within the current parent.
/// @param name Name of the file to search for.
Storage::List::iterator find_file_by_name(std::string_view name);
/// @brief Searches the list for a file matching ID.
/// @param id ID to search for.s
Storage::List::iterator find_file_by_id(std::string_view id);
/// @brief Locates any item (directory/file) by the id passed.
/// @param id ID to search for.
Storage::List::iterator find_item_by_id(std::string_view id);
/// @brief Searches starting with the iterator start for items that belong to parentID
/// @param start Beginning iterator for search.
/// @param parentID ParentID to match.
Storage::List::iterator find_by_parent_id(Storage::List::iterator start, std::string_view parentID);
};
} // namespace remote

60
include/remote/URL.hpp Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <string>
namespace remote
{
/// @brief This is a class to make URLs easier to build for Google Drive and WebDav.
/// @note Normally I don't go this route, but it makes things easier.
class URL final
{
public:
/// @brief Default
URL() = default;
/// @brief Constructs a URL with a base URL already in place.
/// @param base String_view containing the base URL.
URL(std::string_view base);
/// @brief Copy constructor.
/// @param url URL to copy.
URL(const URL &url);
/// @brief Move constructor.
/// @param url URL to move.
URL(URL &&url);
/// @brief Makes a copy of the URL passed.
/// @param url remote::URL instance to make a copy of.
URL &operator=(const URL &url);
/// @brief Move operator.
/// @param url URL to move.
URL &operator=(URL &&url);
/// @brief Sets the base URL. Basically resets the string back to square 0.
/// @param base Base URL to start with.
URL &set_base(std::string_view base);
/// @brief Appends the string passed as a path to the URL
/// @param path Path to append;
URL &append_path(std::string_view path);
/// @brief Appends a string parameter
/// @param param Parameter to append.
/// @param value Value of the parameter to append.
URL &append_parameter(std::string_view param, std::string_view value);
/// @brief Appends a trailing slash if needed.
URL &append_slash();
/// @brief Returns the C string of the url string.
const char *get() const;
private:
/// @brief This is where the actual URL is held.
std::string m_url;
/// @brief This checks and appends the necessary separator to the URL string.
void append_separator();
};
} // namespace remote

68
include/remote/WebDav.hpp Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include "remote/Storage.hpp"
#include "remote/URL.hpp"
#include <string>
namespace remote
{
class WebDav final : public remote::Storage
{
public:
/// @brief Loads the WebDav config from the SD card and loads the listing.
WebDav();
/// @brief Creates a new directory on the WebDav server.
/// @param name Name of the directory to create.
bool create_directory(std::string_view name) override;
/// @brief Uploads a file to the webdav server. File name is retrieved from the path.
/// @param source Local path of the file to upload.
bool upload_file(const fslib::Path &source,
std::string_view remoteName,
sys::ProgressTask *task = nullptr) override;
/// @brief Patches or updates a file on the WebDav server.
/// @param file Pointer to the file to update.
/// @param source Path of the source file to update with.
bool patch_file(remote::Item *file, const fslib::Path &source, sys::ProgressTask *task = nullptr) override;
/// @brief Downloads the passed file from the WebDav server.
/// @param file Pointer to the file to download.
/// @param destination Path to write the downloaded data from.
bool download_file(const remote::Item *item,
const fslib::Path &destination,
sys::ProgressTask *task = nullptr) override;
/// @brief Deletes the target item from the WebDav server.
/// @param item Item to delete.
bool delete_item(const remote::Item *item) override;
/// @brief Renames an item on WebDav.
/// @param item Item to rename.
/// @param newName New name of the item.
bool rename_item(remote::Item *item, std::string_view newName) override;
private:
/// @brief Origin or server address.
std::string m_origin{};
/// @brief Username for curl requests.
std::string m_username{};
/// @brief Password for curl requests.
std::string m_password{};
/// @brief Appends the username and password to a WebDav curl request.
void append_credentials();
/// @brief Requests PROPFIND to the url passed.
/// @param url URL to PROPFIND with.
/// @param xml String to record XML response to.
bool prop_find(const remote::URL &url, std::string &xml);
/// @brief Processes a PROPFIND XML response.
/// @param xml XML response.
bool process_listing(std::string_view xml);
};
} // namespace remote

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