initial commit
This commit is contained in:
BIN
.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx
Normal file
BIN
.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx
Normal file
BIN
.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx
Normal file
BIN
.cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/Plutonium.176CA4CACA9A1290.idx
Normal file
BIN
.cache/clangd/index/Plutonium.176CA4CACA9A1290.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx
Normal file
BIN
.cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx
Normal file
BIN
.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx
Normal file
BIN
.cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx
Normal file
BIN
.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx
Normal file
BIN
.cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx
Normal file
BIN
.cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/const.h.BA34DD522C225A5C.idx
Normal file
BIN
.cache/clangd/index/const.h.BA34DD522C225A5C.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/data.cpp.92E1D4C612A67155.idx
Normal file
BIN
.cache/clangd/index/data.cpp.92E1D4C612A67155.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx
Normal file
BIN
.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx
Normal file
BIN
.cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx
Normal file
BIN
.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx
Normal file
BIN
.cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx
Normal file
BIN
.cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx
Normal file
BIN
.cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx
Normal file
BIN
.cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx
Normal file
BIN
.cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx
Normal file
BIN
.cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx
Normal file
BIN
.cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx
Normal file
BIN
.cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx
Normal file
BIN
.cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/file.cpp.5237F76D2447B003.idx
Normal file
BIN
.cache/clangd/index/file.cpp.5237F76D2447B003.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/file.h.ECED60B58854FB92.idx
Normal file
BIN
.cache/clangd/index/file.h.ECED60B58854FB92.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx
Normal file
BIN
.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx
Normal file
BIN
.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/fsfile.c.867FA10C150715C4.idx
Normal file
BIN
.cache/clangd/index/fsfile.c.867FA10C150715C4.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx
Normal file
BIN
.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx
Normal file
BIN
.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx
Normal file
BIN
.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ldn.h.1D557AF101A575C7.idx
Normal file
BIN
.cache/clangd/index/ldn.h.1D557AF101A575C7.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx
Normal file
BIN
.cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx
Normal file
BIN
.cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx
Normal file
BIN
.cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx
Normal file
BIN
.cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx
Normal file
BIN
.cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx
Normal file
BIN
.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx
Normal file
BIN
.cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/type.h.3039A978BD63D81E.idx
Normal file
BIN
.cache/clangd/index/type.h.3039A978BD63D81E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx
Normal file
BIN
.cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx
Normal file
BIN
.cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx
Normal file
BIN
.cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx
Normal file
BIN
.cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx
Normal file
BIN
.cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx
Normal file
BIN
.cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx
Normal file
BIN
.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/util.h.4B818D27750FB9A3.idx
Normal file
BIN
.cache/clangd/index/util.h.4B818D27750FB9A3.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx
Normal file
BIN
.cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/zip.h.74C41B03268B991E.idx
Normal file
BIN
.cache/clangd/index/zip.h.74C41B03268B991E.idx
Normal file
Binary file not shown.
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "lib"]
|
||||||
|
path = lib
|
||||||
|
url = https://github.com/XorTroll/Plutonium.git
|
||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
18
.idea/misc.xml
generated
Normal file
18
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MakefileSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<MakefileProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="version" value="2" />
|
||||||
|
</MakefileProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
||||||
7
.idea/vcs.xml
generated
Normal file
7
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/lib" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
23
.vscode/c_cpp_properties.json
vendored
Normal file
23
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "DKP aarch64",
|
||||||
|
"includePath": [
|
||||||
|
"${env:DEVKITPRO}/devkitA64/aarch64-none-elf/include/**",
|
||||||
|
"${env:DEVKITPRO}/devkitARM/arm-none-eabi/include/**",
|
||||||
|
"${env:DEVKITPRO}/devkitA64/lib/gcc/aarch64-none-elf/13.2.0/include/**",
|
||||||
|
"${env:DEVKITPRO}/libnx/include/**",
|
||||||
|
"${env:DEVKITPRO}/portlibs/switch/include/**",
|
||||||
|
"${workspaceFolder}/include/**",
|
||||||
|
"${workspaceFolder}/lib/Plutonium/include/**",
|
||||||
|
"/opt/homebrew/opt/lz4/include/**"
|
||||||
|
],
|
||||||
|
"defines": ["SWITCH", "__SWITCH__", "DEBUG", "__BSD_VISIBLE"],
|
||||||
|
"compilerPath": "${env:DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++",
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "c++14",
|
||||||
|
"intelliSenseMode": "linux-gcc-arm64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
101
.vscode/settings.json
vendored
Normal file
101
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"string.h": "c",
|
||||||
|
"stdlib.h": "c",
|
||||||
|
"switch.h": "c",
|
||||||
|
"psm.h": "c",
|
||||||
|
"vector": "cpp",
|
||||||
|
"any": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"barrier": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"cfenv": "cpp",
|
||||||
|
"charconv": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"codecvt": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"coroutine": "cpp",
|
||||||
|
"csetjmp": "cpp",
|
||||||
|
"csignal": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cuchar": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"forward_list": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"expected": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"source_location": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"latch": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"numbers": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"scoped_allocator": "cpp",
|
||||||
|
"semaphore": "cpp",
|
||||||
|
"shared_mutex": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"spanstream": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stacktrace": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"stdfloat": "cpp",
|
||||||
|
"stop_token": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"syncstream": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"typeindex": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"valarray": "cpp",
|
||||||
|
"variant": "cpp",
|
||||||
|
"filesystem": "cpp",
|
||||||
|
"plutonium": "cpp"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Iridium_Icon256.png
Normal file
BIN
Iridium_Icon256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
203
Makefile
Normal file
203
Makefile
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := NXST
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source source/fs lib/Plutonium/source
|
||||||
|
INCLUDES := include include/fs lib/Plutonium/include
|
||||||
|
EXEFS_SRC := exefs_src
|
||||||
|
APP_TITLE := NXST
|
||||||
|
APP_AUTHOR := DragonSpirit
|
||||||
|
APP_VERSION := 01.28.2024
|
||||||
|
ICON := icon.jpg
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS += -g -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -lpu -lz -lminizip -lfreetype -lSDL2_mixer -lopusfile -lopus -lmodplug -lmpg123 -lvorbisidec -logg -lSDL2_ttf -lSDL2_gfx -lSDL2_image -lSDL2 -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lwebp -lpng -ljpeg `sdl2-config --libs` `freetype-config --libs` -lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/lib/Plutonium
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
send: $(BUILD)
|
||||||
|
@nxlink $(TARGET).nro
|
||||||
|
|
||||||
|
debug: $(BUILD)
|
||||||
|
@nxlink -s $(TARGET).nro
|
||||||
|
|
||||||
|
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all : $(OUTPUT).pfs0 $(OUTPUT).nro
|
||||||
|
|
||||||
|
$(OUTPUT).pfs0 : $(OUTPUT).nso
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
10
cleanup
Executable file
10
cleanup
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
rm build/ -r || true
|
||||||
|
|
||||||
|
# TRY to remove Iridium.nsp, ignore if it fails
|
||||||
|
|
||||||
|
rm Iridium.nro || true
|
||||||
|
rm Iridium.nacp || true
|
||||||
|
rm Iridium.elf || true
|
||||||
|
rm Iridium.lst || true
|
||||||
|
|
||||||
|
|
||||||
428
compile_commands.json
Normal file
428
compile_commands.json
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/Main.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/Main.cpp",
|
||||||
|
"-o",
|
||||||
|
"Main.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/Main.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/MainApplication.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/MainApplication.cpp",
|
||||||
|
"-o",
|
||||||
|
"MainApplication.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/MainApplication.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/TitlesLayout.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/TitlesLayout.cpp",
|
||||||
|
"-o",
|
||||||
|
"TitlesLayout.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/TitlesLayout.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/UsersLayout.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/UsersLayout.cpp",
|
||||||
|
"-o",
|
||||||
|
"UsersLayout.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/UsersLayout.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/data.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/data.cpp",
|
||||||
|
"-o",
|
||||||
|
"data.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/data.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/fs.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/fs.cpp",
|
||||||
|
"-o",
|
||||||
|
"fs.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/fs.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/ldn.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/ldn.cpp",
|
||||||
|
"-o",
|
||||||
|
"ldn.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/ldn.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/threads.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/threads.cpp",
|
||||||
|
"-o",
|
||||||
|
"threads.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/threads.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/util.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/util.cpp",
|
||||||
|
"-o",
|
||||||
|
"util.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/util.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/dir.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/fs/dir.cpp",
|
||||||
|
"-o",
|
||||||
|
"dir.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/fs/dir.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/file.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/fs/file.cpp",
|
||||||
|
"-o",
|
||||||
|
"file.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/fs/file.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-g++",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/zip.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-fno-rtti",
|
||||||
|
"-fno-exceptions",
|
||||||
|
"-std=gnu++17",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/fs/zip.cpp",
|
||||||
|
"-o",
|
||||||
|
"zip.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/fs/zip.cpp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/n.fedorov/dev/NXST",
|
||||||
|
"arguments": [
|
||||||
|
"aarch64-none-elf-gcc",
|
||||||
|
"-MMD",
|
||||||
|
"-MP",
|
||||||
|
"-MF",
|
||||||
|
"/Users/n.fedorov/dev/NXST/build/fsfile.d",
|
||||||
|
"-g",
|
||||||
|
"-O2",
|
||||||
|
"-ffunction-sections",
|
||||||
|
"-march=armv8-a+crc+crypto",
|
||||||
|
"-mtune=cortex-a57",
|
||||||
|
"-mtp=soft",
|
||||||
|
"-fPIE",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/include/fs",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/opt/devkitpro/portlibs/switch/include",
|
||||||
|
"-I/opt/devkitpro/libnx/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/lib/Plutonium/include",
|
||||||
|
"-I/Users/n.fedorov/dev/NXST/build",
|
||||||
|
"-D__SWITCH__",
|
||||||
|
"-c",
|
||||||
|
"/Users/n.fedorov/dev/NXST/source/fs/fsfile.c",
|
||||||
|
"-o",
|
||||||
|
"fsfile.o"
|
||||||
|
],
|
||||||
|
"file": "/Users/n.fedorov/dev/NXST/source/fs/fsfile.c"
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
include/.DS_Store
vendored
Normal file
BIN
include/.DS_Store
vendored
Normal file
Binary file not shown.
21
include/MainApplication.hpp
Normal file
21
include/MainApplication.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pu/Plutonium>
|
||||||
|
#include <UsersLayout.hpp>
|
||||||
|
#include <TitlesLayout.hpp>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
class MainApplication : public pu::ui::Application {
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Application::Application;
|
||||||
|
PU_SMART_CTOR(MainApplication)
|
||||||
|
|
||||||
|
void OnLoad() override;
|
||||||
|
|
||||||
|
// Layout instance
|
||||||
|
UsersLayout::Ref usersLayout;
|
||||||
|
TitlesLayout::Ref titlesLayout;
|
||||||
|
};
|
||||||
|
}
|
||||||
19
include/TitlesLayout.hpp
Normal file
19
include/TitlesLayout.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include <pu/Plutonium>
|
||||||
|
#include <const.h>
|
||||||
|
#include <data.h>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
class TitlesLayout : public pu::ui::Layout {
|
||||||
|
private:
|
||||||
|
|
||||||
|
pu::ui::elm::Menu::Ref titlesMenu;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void InitTitles();
|
||||||
|
|
||||||
|
void onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos);
|
||||||
|
|
||||||
|
PU_SMART_CTOR(TitlesLayout)
|
||||||
|
};
|
||||||
|
}
|
||||||
22
include/UsersLayout.hpp
Normal file
22
include/UsersLayout.hpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include <pu/Plutonium>
|
||||||
|
#include <const.h>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
class UsersLayout : public pu::ui::Layout {
|
||||||
|
private:
|
||||||
|
|
||||||
|
pu::ui::elm::Menu::Ref usersMenu;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
UsersLayout();
|
||||||
|
|
||||||
|
void onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos);
|
||||||
|
|
||||||
|
int32_t GetCurrentIndex();
|
||||||
|
|
||||||
|
PU_SMART_CTOR(UsersLayout)
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
2
include/const.h
Normal file
2
include/const.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define BACKGROUND_COLOR COLOR("00FFFFFF")
|
||||||
|
#define COLOR(hex) pu::ui::Color::FromHex(hex)
|
||||||
91
include/data.h
Normal file
91
include/data.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#define BLD_MON 02
|
||||||
|
#define BLD_DAY 23
|
||||||
|
#define BLD_YEAR 2023
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
NacpStruct nacp;
|
||||||
|
std::string title, safeTitle, author;//Shortcuts sorta.
|
||||||
|
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(const AccountUid& _id, const std::string& _backupName, const std::string& _safeBackupName);
|
||||||
|
|
||||||
|
//Sets ID
|
||||||
|
void setUID(const AccountUid& _id);
|
||||||
|
|
||||||
|
//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; }
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
//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);
|
||||||
|
int getTitleIndexInUser(const data::user& u, const uint64_t& tid);
|
||||||
|
extern SetLanguage sysLang;
|
||||||
|
|
||||||
|
}
|
||||||
54
include/fs.h
Normal file
54
include/fs.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <minizip/zip.h>
|
||||||
|
#include <minizip/unzip.h>
|
||||||
|
|
||||||
|
#include "fs/fstype.h"
|
||||||
|
#include "fs/file.h"
|
||||||
|
#include "fs/dir.h"
|
||||||
|
#include "fs/fsfile.h"
|
||||||
|
#include "fs/zip.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(const 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, ...);
|
||||||
|
}
|
||||||
54
include/fs/dir.h
Normal file
54
include/fs/dir.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
64
include/fs/file.h
Normal file
64
include/fs/file.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#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"
|
||||||
|
|
||||||
|
namespace fs
|
||||||
|
{
|
||||||
|
//Copy args are optional and only used if passed and threaded
|
||||||
|
void copyFile(const std::string& src, const std::string& dst, threadInfo *t);
|
||||||
|
void copyFileThreaded(const std::string& src, const std::string& dst);
|
||||||
|
void copyFileCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t);
|
||||||
|
void copyFileCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev);
|
||||||
|
void fileDrawFunc(void *a);
|
||||||
|
|
||||||
|
//deletes file
|
||||||
|
void delfile(const std::string& _p);
|
||||||
|
|
||||||
|
//Dumps all titles for current user
|
||||||
|
void dumpAllUserSaves();
|
||||||
|
|
||||||
|
void getShowFileProps(const std::string& _path);
|
||||||
|
void getShowDirProps(const std::string& _path);
|
||||||
|
|
||||||
|
bool fileExists(const std::string& _path);
|
||||||
|
//Returns file size
|
||||||
|
size_t fsize(const std::string& _f);
|
||||||
|
|
||||||
|
class dataFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
dataFile(const std::string& _path);
|
||||||
|
~dataFile();
|
||||||
|
void close(){ fclose(f); }
|
||||||
|
|
||||||
|
bool isOpen() const { return opened; }
|
||||||
|
|
||||||
|
bool readNextLine(bool proc);
|
||||||
|
//Finds where variable name ends. When a '(' or '=' is hit. Strips spaces
|
||||||
|
void procLine();
|
||||||
|
std::string getLine() const { return line; }
|
||||||
|
std::string getName() const { return name; }
|
||||||
|
//Reads until ';', ',', or '\n' is hit and returns as string.
|
||||||
|
std::string getNextValueStr();
|
||||||
|
int getNextValueInt();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *f;
|
||||||
|
std::string line, name;
|
||||||
|
size_t lPos = 0;
|
||||||
|
bool opened = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void logOpen();
|
||||||
|
void logWrite(const char *fmt, ...);
|
||||||
|
void logClose();
|
||||||
|
}
|
||||||
100
include/fs/fsfile.h
Normal file
100
include/fs/fsfile.h
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//Bare minimum wrapper around switch fs for JKSV
|
||||||
|
#define FS_SEEK_SET 0
|
||||||
|
#define FS_SEEK_CUR 1
|
||||||
|
#define FS_SEEK_END 2
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FsFile _f;
|
||||||
|
Result error;
|
||||||
|
s64 offset, fsize;
|
||||||
|
} FSFILE;
|
||||||
|
|
||||||
|
int fsremove(const char *_p);
|
||||||
|
Result fsDelDirRec(const char *_p);
|
||||||
|
|
||||||
|
char *getDeviceFromPath(char *dev, size_t _max, const char *path);
|
||||||
|
char *getFilePath(char *pathOut, size_t _max, const char *path);
|
||||||
|
|
||||||
|
bool fsMkDir(const char *_p);
|
||||||
|
|
||||||
|
/*Opens file. Device is fetched from path. Libnx romfs doesn't work with this.
|
||||||
|
Mode needs to be:
|
||||||
|
FsOpenMode_Read
|
||||||
|
FsOpenMode_Write
|
||||||
|
FsOpenMode_Append
|
||||||
|
*/
|
||||||
|
bool fsfcreate(const char *_p, int64_t crSize);
|
||||||
|
|
||||||
|
FSFILE *fsfopen(const char *_p, uint32_t mode);
|
||||||
|
|
||||||
|
/*Same as above, but FsFileSystem _s is used. Path cannot have device in it*/
|
||||||
|
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode);
|
||||||
|
|
||||||
|
//Closes _f
|
||||||
|
inline void fsfclose(FSFILE *_f)
|
||||||
|
{
|
||||||
|
if(_f != NULL)
|
||||||
|
{
|
||||||
|
fsFileClose(&_f->_f);
|
||||||
|
free(_f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Seeks like stdio
|
||||||
|
inline void fsfseek(FSFILE *_f, int offset, int origin)
|
||||||
|
{
|
||||||
|
switch(origin)
|
||||||
|
{
|
||||||
|
case FS_SEEK_SET:
|
||||||
|
_f->offset = offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_SEEK_CUR:
|
||||||
|
_f->offset += offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FS_SEEK_END:
|
||||||
|
_f->offset = offset + _f->fsize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns offset
|
||||||
|
inline size_t fsftell(FSFILE *_f) { return _f->offset; }
|
||||||
|
|
||||||
|
//Writes buf to file. Automatically resizes _f to fit buf
|
||||||
|
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f);
|
||||||
|
|
||||||
|
//Reads to buff
|
||||||
|
inline size_t fsfread(void *buf, size_t sz, size_t count, FSFILE *_f)
|
||||||
|
{
|
||||||
|
uint64_t read = 0;
|
||||||
|
_f->error = fsFileRead(&_f->_f, _f->offset, buf, sz * count, 0, &read);
|
||||||
|
_f->offset += read;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Gets byte from file
|
||||||
|
inline char fsfgetc(FSFILE *_f)
|
||||||
|
{
|
||||||
|
char ret = 0;
|
||||||
|
uint64_t read = 0;
|
||||||
|
_f->error = fsFileRead(&_f->_f, _f->offset++, &ret, 1, 0, &read);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Writes byte to file
|
||||||
|
inline void fsfputc(int ch, FSFILE *_f) { fsfwrite(&ch, 1, 1, _f); }
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
46
include/fs/fstype.h
Normal file
46
include/fs/fstype.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include "type.h"
|
||||||
|
#include "ldn.h"
|
||||||
|
|
||||||
|
namespace fs
|
||||||
|
{
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
std::string src, dst, dev;
|
||||||
|
zipFile z;
|
||||||
|
unzFile unz;
|
||||||
|
LDN::LDNCommunicate *comm;
|
||||||
|
bool cleanup = false, trimZipPath = false;
|
||||||
|
uint8_t trimZipPlaces = 0;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
94
include/fs/ldn.h
Normal file
94
include/fs/ldn.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <lz4.h>
|
||||||
|
#include <lz4frame.h>
|
||||||
|
#include <lz4frame_static.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
#define SAVE_DATA_SERVER_PORT 25789
|
||||||
|
#define SOCKET_BUFFER_SIZE 4096
|
||||||
|
#define LENGTH_OF_LISTEN_QUEUE 20
|
||||||
|
|
||||||
|
enum LDN_COMMUNICATE_TYPE {
|
||||||
|
UPDATE_FILE,
|
||||||
|
UPDATE_ABORT,
|
||||||
|
UPDATE_OK,
|
||||||
|
UPDATE_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LZ4_readFile_s {
|
||||||
|
LZ4F_dctx *dctxPtr;
|
||||||
|
int socket;
|
||||||
|
LZ4_byte *srcBuf;
|
||||||
|
size_t srcBufNext;
|
||||||
|
size_t srcBufSize;
|
||||||
|
size_t srcBufMaxSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LZ4_writeFile_s {
|
||||||
|
LZ4F_cctx *cctxPtr;
|
||||||
|
int socket;
|
||||||
|
LZ4_byte *dstBuf;
|
||||||
|
size_t maxWriteSize;
|
||||||
|
size_t dstBufMaxSize;
|
||||||
|
LZ4F_errorCode_t errCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace LDN {
|
||||||
|
typedef struct {
|
||||||
|
// point to server's socket fd;
|
||||||
|
int serverFD;
|
||||||
|
// point to communicate socket fd, send meta data
|
||||||
|
int commFD;
|
||||||
|
// for client to bind with server, communicate create file socket fd
|
||||||
|
struct sockaddr_in serverAddr;
|
||||||
|
} LDNCommunicate;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LDNCommunicate *comm;
|
||||||
|
unsigned int filesize = 0, writeLimit = 0;
|
||||||
|
std::string fullPath;
|
||||||
|
std::mutex bufferLock;
|
||||||
|
std::condition_variable cond;
|
||||||
|
std::vector<uint8_t> sharedBuffer;
|
||||||
|
bool bufferIsFull = false;
|
||||||
|
std::string dst, dev;
|
||||||
|
LZ4_writeFile_s* lz4fWrite;
|
||||||
|
} LDNfcopyArgs;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 type;
|
||||||
|
size_t fsz;
|
||||||
|
} commMeta;
|
||||||
|
|
||||||
|
void destroyLDN();
|
||||||
|
|
||||||
|
LDN::LDNCommunicate *createCommunicate(void);
|
||||||
|
|
||||||
|
void destroyCommunicate(LDNCommunicate *comm);
|
||||||
|
|
||||||
|
Result createLDNServer(LDNCommunicate *comm);
|
||||||
|
|
||||||
|
Result createLDNClient(LDNCommunicate *comm);
|
||||||
|
|
||||||
|
int bindClient(int serverFD);
|
||||||
|
|
||||||
|
int bindServer(sockaddr_in *serverAddr);
|
||||||
|
|
||||||
|
bool waitForOK(int socketfd);
|
||||||
|
bool waitForDONE(int socketfd);
|
||||||
|
void sendOK(int socket_fd);
|
||||||
|
void sendDONE(int socket_fd);
|
||||||
|
void sendAbort(int socket_fd);
|
||||||
|
void reciveMeta(commMeta *meta, int socketfd);
|
||||||
|
void sendMeta(commMeta *meta, int socketfd);
|
||||||
|
void copySaveFileToRemote(const std::string &local, threadInfo *t);
|
||||||
|
void copyRemoteSaveFile(threadInfo *t);
|
||||||
|
};
|
||||||
18
include/fs/zip.h
Normal file
18
include/fs/zip.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <minizip/zip.h>
|
||||||
|
#include <minizip/unzip.h>
|
||||||
|
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
namespace fs
|
||||||
|
{
|
||||||
|
//threadInfo is optional and only used when threaded versions are used
|
||||||
|
void copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int trimPlaces, threadInfo *t);
|
||||||
|
void copyDirToZipThreaded(const std::string& src, zipFile dst, bool trimPath, int trimPlaces);
|
||||||
|
void copyZipToDir(unzFile src, const std::string& dst, const std::string& dev, threadInfo *t);
|
||||||
|
void copyZipToDirThreaded(unzFile src, const std::string& dst, const std::string& dev);
|
||||||
|
uint64_t getZipTotalSize(unzFile unz);
|
||||||
|
bool zipNotEmpty(unzFile unz);
|
||||||
|
}
|
||||||
44
include/threads.h
Normal file
44
include/threads.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
namespace threads {
|
||||||
|
|
||||||
|
//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); }
|
||||||
|
|
||||||
|
threadInfo *newThread(ThreadFunc func, void *args, funcPtr _drawFunc);
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
29
include/type.h
Normal file
29
include/type.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#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;
|
||||||
152
include/util.h
Normal file
152
include/util.h
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
//#include "ui.h"
|
||||||
|
#include "fs/file.h"
|
||||||
|
//#include "gfx.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);
|
||||||
|
|
||||||
|
//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(const 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(const 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);
|
||||||
|
|
||||||
|
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(const uint64_t& _id)
|
||||||
|
{
|
||||||
|
char tmp[18];
|
||||||
|
sprintf(tmp, "%016lX", _id);
|
||||||
|
return std::string(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string getIDStrLower(const uint64_t& _id)
|
||||||
|
{
|
||||||
|
char tmp[18];
|
||||||
|
sprintf(tmp, "%08X", (uint32_t)_id);
|
||||||
|
return std::string(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string generatePathByTID(const uint64_t& tid)
|
||||||
|
{
|
||||||
|
return fs::getWorkDir() + data::getTitleSafeNameByTID(tid) + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getSizeString(const uint64_t& _size);
|
||||||
|
|
||||||
|
inline void createTitleDirectoryByTID(const uint64_t& tid)
|
||||||
|
{
|
||||||
|
std::string makePath = fs::getWorkDir() + data::getTitleSafeNameByTID(tid);
|
||||||
|
mkdir(makePath.c_str(), 777);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result accountDeleteUser(AccountUid *uid);
|
||||||
|
|
||||||
|
void sysBoost();
|
||||||
|
void sysNormal();
|
||||||
|
|
||||||
|
inline bool isApplet()
|
||||||
|
{
|
||||||
|
AppletType type = appletGetAppletType();
|
||||||
|
return type == AppletType_LibraryApplet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkForUpdate(void *a);
|
||||||
|
}
|
||||||
1
lib
Submodule
1
lib
Submodule
Submodule lib added at b56564b70d
BIN
source/.DS_Store
vendored
Normal file
BIN
source/.DS_Store
vendored
Normal file
Binary file not shown.
59
source/Main.cpp
Normal file
59
source/Main.cpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#include <MainApplication.hpp>
|
||||||
|
#include <data.h>
|
||||||
|
#include <fs.h>
|
||||||
|
|
||||||
|
static int nxlink_sock = -1;
|
||||||
|
|
||||||
|
extern "C" void userAppInit() {
|
||||||
|
appletInitialize();
|
||||||
|
hidInitialize();
|
||||||
|
nsInitialize();
|
||||||
|
setsysInitialize();
|
||||||
|
setInitialize();
|
||||||
|
accountInitialize(AccountServiceType_Administrator);
|
||||||
|
pmshellInitialize();
|
||||||
|
socketInitializeDefault();
|
||||||
|
pdmqryInitialize();
|
||||||
|
nxlink_sock = nxlinkStdio();
|
||||||
|
printf("userAppInit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void userAppExit() {
|
||||||
|
if (nxlink_sock != -1) {
|
||||||
|
close(nxlink_sock);
|
||||||
|
}
|
||||||
|
appletExit();
|
||||||
|
hidExit();
|
||||||
|
nsExit();
|
||||||
|
setsysExit();
|
||||||
|
setExit();
|
||||||
|
accountExit();
|
||||||
|
pmshellExit();
|
||||||
|
socketExit();
|
||||||
|
pdmqryExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entrypoint
|
||||||
|
int main() {
|
||||||
|
printf("main\n");
|
||||||
|
// First create our renderer, where one can customize SDL or other stuff's
|
||||||
|
// initialization.
|
||||||
|
auto renderer_opts = pu::ui::render::RendererInitOptions(
|
||||||
|
SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags);
|
||||||
|
renderer_opts.UseImage(pu::ui::render::IMGAllFlags);
|
||||||
|
renderer_opts.UseAudio(pu::ui::render::MixerAllFlags);
|
||||||
|
renderer_opts.UseTTF();
|
||||||
|
|
||||||
|
auto renderer = pu::ui::render::Renderer::New(renderer_opts);
|
||||||
|
|
||||||
|
data::init();
|
||||||
|
fs::init();
|
||||||
|
// Create our main application from the renderer
|
||||||
|
auto main = ui::MainApplication::New(renderer);
|
||||||
|
|
||||||
|
main->Prepare();
|
||||||
|
|
||||||
|
main->Show();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
21
source/MainApplication.cpp
Normal file
21
source/MainApplication.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <switch.h>
|
||||||
|
#include <switch/services/hid.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <MainApplication.hpp>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
MainApplication *mainApp;
|
||||||
|
|
||||||
|
void MainApplication::OnLoad() {
|
||||||
|
mainApp = this;
|
||||||
|
this->usersLayout = UsersLayout::New();
|
||||||
|
this->titlesLayout = TitlesLayout::New();
|
||||||
|
this->usersLayout->SetOnInput(
|
||||||
|
std::bind(&UsersLayout::onInput, this->usersLayout, std::placeholders::_1, std::placeholders::_2,
|
||||||
|
std::placeholders::_3, std::placeholders::_4));
|
||||||
|
this->titlesLayout->SetOnInput(std::bind(&TitlesLayout::onInput, this->titlesLayout,std::placeholders::_1, std::placeholders::_2,
|
||||||
|
std::placeholders::_3, std::placeholders::_4));
|
||||||
|
this->LoadLayout(this->usersLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
71
source/TitlesLayout.cpp
Normal file
71
source/TitlesLayout.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include <MainApplication.hpp>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <data.h>
|
||||||
|
|
||||||
|
static std::vector<uint64_t> accSids, devSids, bcatSids, cacheSids;
|
||||||
|
|
||||||
|
//Sort save create tids alphabetically by title from data
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
bool operator()(const uint64_t& tid1, const uint64_t& tid2)
|
||||||
|
{
|
||||||
|
std::string tid1Title = data::getTitleNameByTID(tid1);
|
||||||
|
std::string tid2Title = data::getTitleNameByTID(tid2);
|
||||||
|
|
||||||
|
uint32_t pointA = 0, pointB = 0;
|
||||||
|
for(unsigned i = 0, j = 0; i < tid1Title.length(); )
|
||||||
|
{
|
||||||
|
ssize_t tid1Cnt = decode_utf8(&pointA, (const uint8_t *)&tid1Title.c_str()[i]);
|
||||||
|
ssize_t tid2Cnt = decode_utf8(&pointB, (const uint8_t *)&tid2Title.c_str()[j]);
|
||||||
|
|
||||||
|
pointA = tolower(pointA), pointB = tolower(pointB);
|
||||||
|
if(pointA != pointB)
|
||||||
|
return pointA < pointB;
|
||||||
|
|
||||||
|
i += tid1Cnt, j += tid2Cnt;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} sortCreateTIDs;
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
extern MainApplication *mainApp;
|
||||||
|
void TitlesLayout::InitTitles() {
|
||||||
|
this->titlesMenu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94, 7);
|
||||||
|
const data::user *u = data::getCurrentUser();
|
||||||
|
for(const data::userTitleInfo& t : u->titleInfo) {
|
||||||
|
auto titleItem = pu::ui::elm::MenuItem::New(data::getTitleNameByTID(t.tid).c_str());
|
||||||
|
titleItem->SetColor(COLOR("#FFFFFFFF"));
|
||||||
|
titlesMenu->AddItem(titleItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->Add(this->titlesMenu);
|
||||||
|
|
||||||
|
this->SetBackgroundColor(BACKGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitlesLayout::onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos) {
|
||||||
|
if (Down & HidNpadButton_Plus) {
|
||||||
|
mainApp->Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Down & HidNpadButton_A) {
|
||||||
|
printf("current game index is %i\n", this->titlesMenu->GetSelectedIndex());
|
||||||
|
data::setTitleIndex(this->titlesMenu->GetSelectedIndex());
|
||||||
|
int opt = mainApp->CreateShowDialog("", "What do you want?", { "Transfer", "Receive" }, true);
|
||||||
|
printf("opt is %d\n", opt);
|
||||||
|
switch (opt) {
|
||||||
|
case 0: {
|
||||||
|
// Transfert selected
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
// Receive selected
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
source/UsersLayout.cpp
Normal file
39
source/UsersLayout.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <data.h>
|
||||||
|
#include <MainApplication.hpp>
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
extern MainApplication *mainApp;
|
||||||
|
|
||||||
|
UsersLayout::UsersLayout() : Layout::Layout() {
|
||||||
|
this->usersMenu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94,
|
||||||
|
7);
|
||||||
|
this->usersMenu->SetScrollbarColor(COLOR("#170909FF"));
|
||||||
|
|
||||||
|
for (data::user &u: data::users) {
|
||||||
|
auto username = pu::ui::elm::MenuItem::New(u.getUsername() + ": " + std::to_string(u.titleInfo.size()));
|
||||||
|
username->SetColor(COLOR("#FFFFFFFF"));
|
||||||
|
this->usersMenu->AddItem(username);
|
||||||
|
}
|
||||||
|
this->SetBackgroundColor(BACKGROUND_COLOR);
|
||||||
|
this->Add(this->usersMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t UsersLayout::GetCurrentIndex() {
|
||||||
|
return this->usersMenu->GetSelectedIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsersLayout::onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos) {
|
||||||
|
if (Down & HidNpadButton_Plus) {
|
||||||
|
mainApp->Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Down & HidNpadButton_A) {
|
||||||
|
printf("current index is %i\n", this->usersMenu->GetSelectedIndex());
|
||||||
|
data::setUserIndex(this->usersMenu->GetSelectedIndex());
|
||||||
|
mainApp->titlesLayout->InitTitles();
|
||||||
|
mainApp->LoadLayout(mainApp->titlesLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
352
source/data.cpp
Normal file
352
source/data.cpp
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include "fs/file.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
//FsSaveDataSpaceId_All doesn't work for SD
|
||||||
|
static const unsigned saveOrder[] = {0, 1, 2, 3, 4, 100, 101};
|
||||||
|
|
||||||
|
int selUser = 0, selData = 0;
|
||||||
|
|
||||||
|
//User vector
|
||||||
|
std::vector <data::user> data::users;
|
||||||
|
|
||||||
|
//For other save types
|
||||||
|
static bool sysBCATPushed = false, tempPushed = false;
|
||||||
|
std::unordered_map <uint64_t, data::titleInfo> data::titles;
|
||||||
|
|
||||||
|
//Sorts titles by sortType
|
||||||
|
static struct {
|
||||||
|
bool operator()(const data::userTitleInfo &a, const data::userTitleInfo &b) {
|
||||||
|
std::string titleA = data::getTitleNameByTID(a.tid);
|
||||||
|
std::string titleB = data::getTitleNameByTID(b.tid);
|
||||||
|
uint32_t pointA, pointB;
|
||||||
|
for (unsigned i = 0, j = 0; i < titleA.length();) {
|
||||||
|
ssize_t aCnt = decode_utf8(&pointA, (const uint8_t *) &titleA.data()[i]);
|
||||||
|
ssize_t bCnt = decode_utf8(&pointB, (const uint8_t *) &titleB.data()[j]);
|
||||||
|
pointA = tolower(pointA), pointB = tolower(pointB);
|
||||||
|
if (pointA != pointB)
|
||||||
|
return pointA < pointB;
|
||||||
|
|
||||||
|
i += aCnt;
|
||||||
|
j += bCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} sortTitles;
|
||||||
|
|
||||||
|
//Returns -1 for new
|
||||||
|
static int getUserIndex(const AccountUid &id) {
|
||||||
|
u128 nId = util::accountUIDToU128(id);
|
||||||
|
for (unsigned i = 0; i < data::users.size(); i++)
|
||||||
|
if (data::users[i].getUID128() == nId) return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool accountSystemSaveCheck(const FsSaveDataInfo &_inf) {
|
||||||
|
if (_inf.save_data_type == FsSaveDataType_System && util::accountUIDToU128(_inf.uid) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Minimal init/test to avoid loading and creating things I don't need
|
||||||
|
static bool testMount(const FsSaveDataInfo &_inf) {
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if ((ret = fs::mountSave(_inf)))
|
||||||
|
fs::unmountSave();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void addTitleToList(const uint64_t &tid) {
|
||||||
|
uint64_t outSize = 0;
|
||||||
|
NsApplicationControlData *ctrlData = new NsApplicationControlData;
|
||||||
|
NacpLanguageEntry *ent;
|
||||||
|
Result ctrlRes = nsGetApplicationControlData(NsApplicationControlSource_Storage, tid, ctrlData,
|
||||||
|
sizeof(NsApplicationControlData), &outSize);
|
||||||
|
Result nacpRes = nacpGetLanguageEntry(&ctrlData->nacp, &ent);
|
||||||
|
size_t iconSize = outSize - sizeof(ctrlData->nacp);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(ctrlRes) && !(outSize < sizeof(ctrlData->nacp)) && R_SUCCEEDED(nacpRes) && iconSize > 0) {
|
||||||
|
//Copy nacp
|
||||||
|
memcpy(&data::titles[tid].nacp, &ctrlData->nacp, sizeof(NacpStruct));
|
||||||
|
|
||||||
|
//Setup 'shortcuts' to strings
|
||||||
|
NacpLanguageEntry *ent;
|
||||||
|
nacpGetLanguageEntry(&data::titles[tid].nacp, &ent);
|
||||||
|
if (strlen(ent->name) == 0)
|
||||||
|
data::titles[tid].title = ctrlData->nacp.lang[SetLanguage_ENUS].name;
|
||||||
|
else
|
||||||
|
data::titles[tid].title = ent->name;
|
||||||
|
|
||||||
|
data::titles[tid].author = ent->author;
|
||||||
|
|
||||||
|
if ((data::titles[tid].safeTitle = util::safeString(ent->name)) == "")
|
||||||
|
data::titles[tid].safeTitle = util::getIDStr(tid);
|
||||||
|
} else {
|
||||||
|
memset(&data::titles[tid].nacp, 0, sizeof(NacpStruct));
|
||||||
|
data::titles[tid].title = util::getIDStr(tid);
|
||||||
|
data::titles[tid].author = "Someone?";
|
||||||
|
data::titles[tid].safeTitle = util::getIDStr(tid);
|
||||||
|
}
|
||||||
|
delete ctrlData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool titleIsLoaded(const uint64_t &tid) {
|
||||||
|
auto findTid = data::titles.find(tid);
|
||||||
|
|
||||||
|
return findTid == data::titles.end() ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadUserAccounts() {
|
||||||
|
s32 total = 0;
|
||||||
|
AccountUid *uids = new AccountUid[8];
|
||||||
|
if (R_SUCCEEDED(accountListAllUsers(uids, 8, &total))) {
|
||||||
|
for (int i = 0; i < total; i++)
|
||||||
|
data::users.emplace_back(uids[i], "", "");
|
||||||
|
}
|
||||||
|
delete[] uids;
|
||||||
|
}
|
||||||
|
|
||||||
|
//This can load titles installed without having save data
|
||||||
|
static void loadTitlesFromRecords() {
|
||||||
|
NsApplicationRecord nsRecord;
|
||||||
|
int32_t entryCount = 0, recordOffset = 0;
|
||||||
|
while (R_SUCCEEDED(nsListApplicationRecord(&nsRecord, 1, recordOffset++, &entryCount)) && entryCount > 0) {
|
||||||
|
if (!titleIsLoaded(nsRecord.application_id))
|
||||||
|
addTitleToList(nsRecord.application_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool data::loadUsersTitles(bool clearUsers) {
|
||||||
|
static unsigned systemUserCount = 4;
|
||||||
|
FsSaveDataInfoReader it;
|
||||||
|
FsSaveDataInfo info;
|
||||||
|
s64 total = 0;
|
||||||
|
|
||||||
|
//Clear titles
|
||||||
|
for (data::user &u: data::users)
|
||||||
|
u.titleInfo.clear();
|
||||||
|
if (clearUsers) {
|
||||||
|
systemUserCount = 4;
|
||||||
|
data::users.clear();
|
||||||
|
|
||||||
|
loadUserAccounts();
|
||||||
|
sysBCATPushed = false;
|
||||||
|
tempPushed = false;
|
||||||
|
|
||||||
|
users.emplace_back(util::u128ToAccountUID(3), "Device", "Device");
|
||||||
|
users.emplace_back(util::u128ToAccountUID(2), "BCAT", "BCAT");
|
||||||
|
users.emplace_back(util::u128ToAccountUID(5), "Cache", "Cache");
|
||||||
|
users.emplace_back(util::u128ToAccountUID(0), "System", "System");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 7; i++) {
|
||||||
|
if (R_FAILED(fsOpenSaveDataInfoReader(&it, (FsSaveDataSpaceId) saveOrder[i])))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (R_SUCCEEDED(fsSaveDataInfoReaderRead(&it, &info, 1, &total)) && total != 0) {
|
||||||
|
uint64_t tid = 0;
|
||||||
|
if (info.save_data_type == FsSaveDataType_System || info.save_data_type == FsSaveDataType_SystemBcat)
|
||||||
|
tid = info.system_save_data_id;
|
||||||
|
else
|
||||||
|
tid = info.application_id;
|
||||||
|
|
||||||
|
if (!titleIsLoaded(tid))
|
||||||
|
addTitleToList(tid);
|
||||||
|
|
||||||
|
//Don't bother with this stuff
|
||||||
|
if (!accountSystemSaveCheck(info) || !testMount(info))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (info.save_data_type) {
|
||||||
|
case FsSaveDataType_Bcat:
|
||||||
|
info.uid = util::u128ToAccountUID(2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Device:
|
||||||
|
info.uid = util::u128ToAccountUID(3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_SystemBcat:
|
||||||
|
info.uid = util::u128ToAccountUID(4);
|
||||||
|
if (!sysBCATPushed) {
|
||||||
|
++systemUserCount;
|
||||||
|
sysBCATPushed = true;
|
||||||
|
users.emplace_back(util::u128ToAccountUID(4), "System BCAT",
|
||||||
|
"System BCAT");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Cache:
|
||||||
|
info.uid = util::u128ToAccountUID(5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Temporary:
|
||||||
|
info.uid = util::u128ToAccountUID(6);
|
||||||
|
if (!tempPushed) {
|
||||||
|
++systemUserCount;
|
||||||
|
tempPushed = true;
|
||||||
|
users.emplace_back(util::u128ToAccountUID(6), "Temporary",
|
||||||
|
"Temporary");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int u = getUserIndex(info.uid);
|
||||||
|
if (u == -1) {
|
||||||
|
users.emplace(data::users.end() - systemUserCount, info.uid, "", "");
|
||||||
|
u = getUserIndex(info.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PdmPlayStatistics playStats;
|
||||||
|
if (info.save_data_type == FsSaveDataType_Account || info.save_data_type == FsSaveDataType_Device)
|
||||||
|
pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(info.application_id, info.uid, false,
|
||||||
|
&playStats);
|
||||||
|
else
|
||||||
|
memset(&playStats, 0, sizeof(PdmPlayStatistics));
|
||||||
|
users[u].addUserTitleInfo(tid, &info, &playStats);
|
||||||
|
}
|
||||||
|
fsSaveDataInfoReaderClose(&it);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get reference to device save user
|
||||||
|
unsigned devPos = getUserIndex(util::u128ToAccountUID(3));
|
||||||
|
data::user &dev = data::users[devPos];
|
||||||
|
for (unsigned i = 0; i < devPos; i++) {
|
||||||
|
//Not needed but makes this easier to read
|
||||||
|
data::user &u = data::users[i];
|
||||||
|
u.titleInfo.insert(u.titleInfo.end(), dev.titleInfo.begin(), dev.titleInfo.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
data::sortUserTitles();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::sortUserTitles() {
|
||||||
|
|
||||||
|
for (data::user &u: data::users)
|
||||||
|
std::sort(u.titleInfo.begin(), u.titleInfo.end(), sortTitles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::init() {
|
||||||
|
data::loadUsersTitles(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::exit() {
|
||||||
|
/*Still needed for planned future revisions*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::setUserIndex(unsigned _sUser) {
|
||||||
|
selUser = _sUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
data::user *data::getCurrentUser() {
|
||||||
|
return &users[selUser];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned data::getCurrentUserIndex() {
|
||||||
|
return selUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::setTitleIndex(unsigned _sTitle) {
|
||||||
|
selData = _sTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
data::userTitleInfo *data::getCurrentUserTitleInfo() {
|
||||||
|
return &users[selUser].titleInfo[selData];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned data::getCurrentUserTitleInfoIndex() {
|
||||||
|
return selData;
|
||||||
|
}
|
||||||
|
|
||||||
|
data::titleInfo *data::getTitleInfoByTID(const uint64_t &tid) {
|
||||||
|
if (titles.find(tid) != titles.end())
|
||||||
|
return &titles[tid];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string data::getTitleNameByTID(const uint64_t &tid) {
|
||||||
|
return titles[tid].title;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string data::getTitleSafeNameByTID(const uint64_t &tid) {
|
||||||
|
return titles[tid].safeTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int data::getTitleIndexInUser(const data::user &u, const uint64_t &tid) {
|
||||||
|
for (unsigned i = 0; i < u.titleInfo.size(); i++) {
|
||||||
|
if (u.titleInfo[i].tid == tid)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data::user::user(const AccountUid &_id, const std::string &_backupName, const std::string &_safeBackupName) {
|
||||||
|
userID = _id;
|
||||||
|
uID128 = util::accountUIDToU128(_id);
|
||||||
|
|
||||||
|
AccountProfile prof;
|
||||||
|
AccountProfileBase base;
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(accountGetProfile(&prof, userID)) && R_SUCCEEDED(accountProfileGet(&prof, NULL, &base))) {
|
||||||
|
username = base.nickname;
|
||||||
|
userSafe = util::safeString(username);
|
||||||
|
if (userSafe.empty()) {
|
||||||
|
char tmp[32];
|
||||||
|
sprintf(tmp, "Acc%08X", (uint32_t) uID128);
|
||||||
|
userSafe = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t jpgSize = 0;
|
||||||
|
accountProfileGetImageSize(&prof, &jpgSize);
|
||||||
|
uint8_t *jpegData = new uint8_t[jpgSize];
|
||||||
|
accountProfileLoadImage(&prof, jpegData, jpgSize, &jpgSize);
|
||||||
|
delete[] jpegData;
|
||||||
|
|
||||||
|
accountProfileClose(&prof);
|
||||||
|
} else {
|
||||||
|
username = _backupName.empty() ? util::getIDStr((uint64_t) uID128) : _backupName;
|
||||||
|
userSafe = _safeBackupName.empty() ? util::getIDStr((uint64_t) uID128) : _safeBackupName;
|
||||||
|
}
|
||||||
|
titles.reserve(64);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::user::setUID(const AccountUid &_id) {
|
||||||
|
userID = _id;
|
||||||
|
uID128 = util::accountUIDToU128(_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::user::addUserTitleInfo(const uint64_t &tid, const FsSaveDataInfo *_saveInfo, const PdmPlayStatistics *_stats) {
|
||||||
|
data::userTitleInfo newInfo;
|
||||||
|
newInfo.tid = tid;
|
||||||
|
memcpy(&newInfo.saveInfo, _saveInfo, sizeof(FsSaveDataInfo));
|
||||||
|
memcpy(&newInfo.playStats, _stats, sizeof(PdmPlayStatistics));
|
||||||
|
titleInfo.push_back(newInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void data::dispStats() {
|
||||||
|
data::user *cu = data::getCurrentUser();
|
||||||
|
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||||
|
|
||||||
|
//Easiest/laziest way to do this
|
||||||
|
std::string stats = std::to_string(users.size()) + "\n";
|
||||||
|
for (data::user &u: data::users) {
|
||||||
|
stats += u.getUsername() + ": " + std::to_string(u.titleInfo.size()) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
519
source/fs.cpp
Normal file
519
source/fs.cpp
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include "fs.h"
|
||||||
|
#include "threads.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static std::string wd = "sdmc:/NXST/";
|
||||||
|
|
||||||
|
static FSFILE *debLog;
|
||||||
|
|
||||||
|
static FsFileSystem sv;
|
||||||
|
|
||||||
|
static std::vector <std::string> pathFilter;
|
||||||
|
|
||||||
|
void fs::init() {
|
||||||
|
mkDirRec(wd);
|
||||||
|
fs::logOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::mountSave(const FsSaveDataInfo &_m) {
|
||||||
|
Result svOpen;
|
||||||
|
FsSaveDataAttribute attr = {0};
|
||||||
|
switch (_m.save_data_type) {
|
||||||
|
case FsSaveDataType_System:
|
||||||
|
case FsSaveDataType_SystemBcat: {
|
||||||
|
attr.uid = _m.uid;
|
||||||
|
attr.system_save_data_id = _m.system_save_data_id;
|
||||||
|
attr.save_data_type = _m.save_data_type;
|
||||||
|
svOpen = fsOpenSaveDataFileSystemBySystemSaveDataId(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Account: {
|
||||||
|
attr.uid = _m.uid;
|
||||||
|
attr.application_id = _m.application_id;
|
||||||
|
attr.save_data_type = _m.save_data_type;
|
||||||
|
attr.save_data_rank = _m.save_data_rank;
|
||||||
|
attr.save_data_index = _m.save_data_index;
|
||||||
|
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Device: {
|
||||||
|
attr.application_id = _m.application_id;
|
||||||
|
attr.save_data_type = FsSaveDataType_Device;
|
||||||
|
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Bcat: {
|
||||||
|
attr.application_id = _m.application_id;
|
||||||
|
attr.save_data_type = FsSaveDataType_Bcat;
|
||||||
|
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Cache: {
|
||||||
|
attr.application_id = _m.application_id;
|
||||||
|
attr.save_data_type = FsSaveDataType_Cache;
|
||||||
|
attr.save_data_index = _m.save_data_index;
|
||||||
|
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Temporary: {
|
||||||
|
attr.application_id = _m.application_id;
|
||||||
|
attr.save_data_type = _m.save_data_type;
|
||||||
|
svOpen = fsOpenSaveDataFileSystem(&sv, (FsSaveDataSpaceId) _m.save_data_space_id, &attr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
svOpen = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return R_SUCCEEDED(svOpen) && fsdevMountDevice("sv", sv) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::commitToDevice(const std::string &dev) {
|
||||||
|
bool ret = true;
|
||||||
|
Result res = fsdevCommitDevice(dev.c_str());
|
||||||
|
if (R_FAILED(res)) {
|
||||||
|
fs::logWrite("Error committing file to device -> 0x%X\n", res);
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fs::getWorkDir() { return wd; }
|
||||||
|
|
||||||
|
void fs::setWorkDir(const std::string &_w) { wd = _w; }
|
||||||
|
|
||||||
|
|
||||||
|
void fs::loadPathFilters(const uint64_t &tid) {
|
||||||
|
char path[256];
|
||||||
|
// sprintf(path, "sdmc:/config/JKSV/0x%016lX_filter.txt", tid);
|
||||||
|
if (fs::fileExists(path)) {
|
||||||
|
fs::dataFile filter(path);
|
||||||
|
while (filter.readNextLine(false))
|
||||||
|
pathFilter.push_back(filter.getLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::pathIsFiltered(const std::string &_path) {
|
||||||
|
if (pathFilter.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (std::string &_p: pathFilter) {
|
||||||
|
if (_path == _p)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::freePathFilters() {
|
||||||
|
pathFilter.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::createSaveData(FsSaveDataType _type, uint64_t _tid, AccountUid _uid, threadInfo *t) {
|
||||||
|
data::titleInfo *tinfo = data::getTitleInfoByTID(_tid);
|
||||||
|
if (t)
|
||||||
|
t->status->setStatus("Creating Save Data for #%s#...", tinfo->title.c_str());
|
||||||
|
|
||||||
|
uint16_t cacheIndex = 0;
|
||||||
|
std::string indexStr;
|
||||||
|
if (_type == FsSaveDataType_Cache &&
|
||||||
|
!(indexStr = util::getStringInput(SwkbdType_NumPad, "0", "Enter cache index", 2, 0, NULL)).empty())
|
||||||
|
cacheIndex = strtoul(indexStr.c_str(), NULL, 10);
|
||||||
|
else if (_type == FsSaveDataType_Cache && indexStr.empty()) {
|
||||||
|
if (t)
|
||||||
|
t->finished = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FsSaveDataAttribute attr;
|
||||||
|
memset(&attr, 0, sizeof(FsSaveDataAttribute));
|
||||||
|
attr.application_id = _tid;
|
||||||
|
attr.uid = _uid;
|
||||||
|
attr.system_save_data_id = 0;
|
||||||
|
attr.save_data_type = _type;
|
||||||
|
attr.save_data_rank = 0;
|
||||||
|
attr.save_data_index = cacheIndex;
|
||||||
|
|
||||||
|
FsSaveDataCreationInfo crt;
|
||||||
|
memset(&crt, 0, sizeof(FsSaveDataCreationInfo));
|
||||||
|
int64_t saveSize = 0, journalSize = 0;
|
||||||
|
switch (_type) {
|
||||||
|
case FsSaveDataType_Account:
|
||||||
|
saveSize = tinfo->nacp.user_account_save_data_size;
|
||||||
|
journalSize = tinfo->nacp.user_account_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Device:
|
||||||
|
saveSize = tinfo->nacp.device_save_data_size;
|
||||||
|
journalSize = tinfo->nacp.device_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Bcat:
|
||||||
|
saveSize = tinfo->nacp.bcat_delivery_cache_storage_size;
|
||||||
|
journalSize = tinfo->nacp.bcat_delivery_cache_storage_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Cache:
|
||||||
|
saveSize = 32 * 1024 * 1024;
|
||||||
|
if (tinfo->nacp.cache_storage_journal_size > tinfo->nacp.cache_storage_data_and_journal_size_max)
|
||||||
|
journalSize = tinfo->nacp.cache_storage_journal_size;
|
||||||
|
else
|
||||||
|
journalSize = tinfo->nacp.cache_storage_data_and_journal_size_max;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (t)
|
||||||
|
t->finished = true;
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crt.save_data_size = saveSize;
|
||||||
|
crt.journal_size = journalSize;
|
||||||
|
crt.available_size = 0x4000;
|
||||||
|
crt.owner_id = _type == FsSaveDataType_Bcat ? 0x010000000000000C : tinfo->nacp.save_data_owner_id;
|
||||||
|
crt.flags = 0;
|
||||||
|
crt.save_data_space_id = FsSaveDataSpaceId_User;
|
||||||
|
|
||||||
|
FsSaveDataMetaInfo meta;
|
||||||
|
memset(&meta, 0, sizeof(FsSaveDataMetaInfo));
|
||||||
|
if (_type != FsSaveDataType_Bcat) {
|
||||||
|
meta.size = 0x40060;
|
||||||
|
meta.type = FsSaveDataMetaType_Thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result res = 0;
|
||||||
|
if (R_SUCCEEDED(res = fsCreateSaveDataFileSystem(&attr, &crt, &meta))) {
|
||||||
|
util::createTitleDirectoryByTID(_tid);
|
||||||
|
data::loadUsersTitles(false);
|
||||||
|
} else {
|
||||||
|
fs::logWrite("SaveCreate Failed -> %X\n", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void createSaveData_t(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
fs::svCreateArgs *crt = (fs::svCreateArgs *) t->argPtr;
|
||||||
|
fs::createSaveData(crt->type, crt->tid, crt->account, t);
|
||||||
|
delete crt;
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::createSaveDataThreaded(FsSaveDataType _type, uint64_t _tid, AccountUid _uid) {
|
||||||
|
fs::svCreateArgs *send = new fs::svCreateArgs;
|
||||||
|
send->type = _type;
|
||||||
|
send->tid = _tid;
|
||||||
|
send->account = _uid;
|
||||||
|
threads::newThread(createSaveData_t, send, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::extendSaveData(const data::userTitleInfo *tinfo, uint64_t extSize, threadInfo *t) {
|
||||||
|
if (t)
|
||||||
|
t->status->setStatus("Extending Save Data for #%s#...", data::getTitleNameByTID(tinfo->tid).c_str());
|
||||||
|
|
||||||
|
uint64_t journal = fs::getJournalSizeMax(tinfo);
|
||||||
|
uint64_t saveID = tinfo->saveInfo.save_data_id;
|
||||||
|
FsSaveDataSpaceId space = (FsSaveDataSpaceId) tinfo->saveInfo.save_data_space_id;
|
||||||
|
Result res = 0;
|
||||||
|
if (R_FAILED((res = fsExtendSaveDataFileSystem(space, saveID, extSize, journal)))) {
|
||||||
|
int64_t totalSize = 0;
|
||||||
|
fs::mountSave(tinfo->saveInfo);
|
||||||
|
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &totalSize);
|
||||||
|
fs::unmountSave();
|
||||||
|
|
||||||
|
fs::logWrite("Extend Failed: %uMB to %uMB -> %X\n", totalSize / 1024 / 1024, extSize / 1024 / 1024, res);
|
||||||
|
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataExtendFailed", 0));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void extendSaveData_t(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
fs::svExtendArgs *e = (fs::svExtendArgs *) t->argPtr;
|
||||||
|
fs::extendSaveData(e->tinfo, e->extSize, t);
|
||||||
|
delete e;
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::extendSaveDataThreaded(const data::userTitleInfo *tinfo, uint64_t extSize) {
|
||||||
|
fs::svExtendArgs *send = new fs::svExtendArgs;
|
||||||
|
send->tinfo = tinfo;
|
||||||
|
send->extSize = extSize;
|
||||||
|
threads::newThread(extendSaveData_t, send, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fs::getJournalSize(const data::userTitleInfo *tinfo) {
|
||||||
|
uint64_t ret = 0;
|
||||||
|
data::titleInfo *t = data::getTitleInfoByTID(tinfo->tid);
|
||||||
|
switch (tinfo->saveInfo.save_data_type) {
|
||||||
|
case FsSaveDataType_Account:
|
||||||
|
ret = t->nacp.user_account_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Device:
|
||||||
|
ret = t->nacp.device_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Bcat:
|
||||||
|
ret = t->nacp.bcat_delivery_cache_storage_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Cache:
|
||||||
|
if (t->nacp.cache_storage_journal_size > 0)
|
||||||
|
ret = t->nacp.cache_storage_journal_size;
|
||||||
|
else
|
||||||
|
ret = t->nacp.cache_storage_data_and_journal_size_max;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = BUFF_SIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fs::getJournalSizeMax(const data::userTitleInfo *tinfo) {
|
||||||
|
uint64_t ret = 0;
|
||||||
|
data::titleInfo *extend = data::getTitleInfoByTID(tinfo->tid);
|
||||||
|
switch (tinfo->saveInfo.save_data_type) {
|
||||||
|
case FsSaveDataType_Account:
|
||||||
|
if (extend->nacp.user_account_save_data_journal_size_max > extend->nacp.user_account_save_data_journal_size)
|
||||||
|
ret = extend->nacp.user_account_save_data_journal_size_max;
|
||||||
|
else
|
||||||
|
ret = extend->nacp.user_account_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Bcat:
|
||||||
|
ret = extend->nacp.bcat_delivery_cache_storage_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Cache:
|
||||||
|
if (extend->nacp.cache_storage_data_and_journal_size_max > extend->nacp.cache_storage_journal_size)
|
||||||
|
ret = extend->nacp.cache_storage_data_and_journal_size_max;
|
||||||
|
else
|
||||||
|
ret = extend->nacp.cache_storage_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FsSaveDataType_Device:
|
||||||
|
if (extend->nacp.device_save_data_journal_size_max > extend->nacp.device_save_data_journal_size)
|
||||||
|
ret = extend->nacp.device_save_data_journal_size_max;
|
||||||
|
else
|
||||||
|
ret = extend->nacp.device_save_data_journal_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
//will just fail
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wipeSave_t(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
t->status->setStatus("Resetting save data...");
|
||||||
|
fs::delDir("sv:/");
|
||||||
|
fs::commitToDevice("sv");
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::wipeSave() {
|
||||||
|
threads::newThread(wipeSave_t, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::createNewBackup(void *a) {
|
||||||
|
if (!fs::dirNotEmpty("sv:/")) {
|
||||||
|
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popSaveIsEmpty", 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t held = threads::padKeysHeld();
|
||||||
|
|
||||||
|
data::user *u = data::getCurrentUser();
|
||||||
|
data::userTitleInfo *d = data::getCurrentUserTitleInfo();
|
||||||
|
data::titleInfo *t = data::getTitleInfoByTID(d->tid);
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
const std::string dict[] =
|
||||||
|
{
|
||||||
|
util::getDateTime(util::DATE_FMT_YMD),
|
||||||
|
util::getDateTime(util::DATE_FMT_YDM),
|
||||||
|
util::getDateTime(util::DATE_FMT_HOYSTE),
|
||||||
|
util::getDateTime(util::DATE_FMT_JHK),
|
||||||
|
util::getDateTime(util::DATE_FMT_ASC),
|
||||||
|
u->getUsernameSafe(),
|
||||||
|
t->safeTitle,
|
||||||
|
util::generateAbbrev(d->tid),
|
||||||
|
".zip"
|
||||||
|
};
|
||||||
|
std::string defaultText = u->getUsernameSafe() + " - " + util::getDateTime(util::DATE_FMT_YMD);
|
||||||
|
out = util::getStringInput(SwkbdType_QWERTY, defaultText, "Enter a new name", 64, 9, dict);
|
||||||
|
out = util::safeString(out);
|
||||||
|
|
||||||
|
if (!out.empty()) {
|
||||||
|
std::string ext = util::getExtensionFromString(out);
|
||||||
|
std::string path = util::generatePathByTID(d->tid) + out;
|
||||||
|
|
||||||
|
fs::mkDir(path);
|
||||||
|
path += "/";
|
||||||
|
fs::copyDirToDirThreaded("sv:/", path);
|
||||||
|
// ui::fldRefreshMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::overwriteBackup(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
std::string *dst = (std::string *) t->argPtr;
|
||||||
|
bool saveHasFiles = fs::dirNotEmpty("sv:/");
|
||||||
|
if (fs::isDir(*dst) && saveHasFiles) {
|
||||||
|
fs::delDir(*dst);
|
||||||
|
fs::mkDir(*dst);
|
||||||
|
dst->append("/");
|
||||||
|
fs::copyDirToDirThreaded("sv:/", *dst);
|
||||||
|
}
|
||||||
|
delete dst;
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::restoreBackup(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
std::string *restore = (std::string *) t->argPtr;
|
||||||
|
data::user *u = data::getCurrentUser();
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
if ((utinfo->saveInfo.save_data_type != FsSaveDataType_System)) {
|
||||||
|
bool saveHasFiles = fs::dirNotEmpty("sv:/");
|
||||||
|
if (saveHasFiles) {
|
||||||
|
std::string autoFolder = util::generatePathByTID(utinfo->tid) + "/AUTO - " + u->getUsernameSafe() + " - " +
|
||||||
|
util::getDateTime(util::DATE_FMT_YMD) + "/";
|
||||||
|
fs::mkDir(autoFolder.substr(0, autoFolder.length() - 1));
|
||||||
|
fs::copyDirToDirThreaded("sv:/", autoFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs::isDir(*restore)) {
|
||||||
|
restore->append("/");
|
||||||
|
if (fs::dirNotEmpty(*restore)) {
|
||||||
|
t->status->setStatus("Calculating save data size...");
|
||||||
|
unsigned dirCount = 0, fileCount = 0;
|
||||||
|
uint64_t saveSize = 0;
|
||||||
|
int64_t availSize = 0;
|
||||||
|
fs::getDirProps(*restore, dirCount, fileCount, saveSize);
|
||||||
|
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
|
||||||
|
if ((int) saveSize > availSize) {
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
fs::unmountSave();
|
||||||
|
fs::extendSaveData(utinfo, saveSize + 0x500000, t);
|
||||||
|
fs::mountSave(utinfo->saveInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::wipeSave();
|
||||||
|
fs::copyDirToDirCommitThreaded(*restore, "sv:/", "sv");
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("popFolderIsEmpty", 0));
|
||||||
|
} else {
|
||||||
|
std::string dstPath = "sv:/" + util::getFilenameFromPath(*restore);
|
||||||
|
fs::copyFileCommitThreaded(*restore, dstPath, "sv");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete restore;
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::deleteBackup(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
std::string *deletePath = (std::string *) t->argPtr;
|
||||||
|
std::string backupName = util::getFilenameFromPath(*deletePath);
|
||||||
|
|
||||||
|
t->status->setStatus("Deleting...");
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
|
||||||
|
if (fs::isDir(*deletePath)) {
|
||||||
|
*deletePath += "/";
|
||||||
|
fs::delDir(*deletePath);
|
||||||
|
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
|
||||||
|
} else {
|
||||||
|
fs::delfile(*deletePath);
|
||||||
|
// ui::showPopMessage(POP_FRAME_DEFAULT, ui::getUICString("saveDataBackupDeleted", 0), backupName.c_str());
|
||||||
|
}
|
||||||
|
// ui::fldRefreshMenu();
|
||||||
|
delete deletePath;
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::dumpAllUserSaves(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
|
||||||
|
t->argPtr = c;
|
||||||
|
data::user *u = data::getCurrentUser();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < u->titleInfo.size(); i++) {
|
||||||
|
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
|
||||||
|
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
|
||||||
|
if (saveMounted && fs::dirNotEmpty("sv:/")) {
|
||||||
|
fs::loadPathFilters(u->titleInfo[i].tid);
|
||||||
|
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
|
||||||
|
util::getDateTime(util::DATE_FMT_YMD) + "/";
|
||||||
|
fs::mkDir(dst.substr(0, dst.length() - 1));
|
||||||
|
fs::copyDirToDir("sv:/", dst, t);
|
||||||
|
fs::freePathFilters();
|
||||||
|
}
|
||||||
|
fs::unmountSave();
|
||||||
|
}
|
||||||
|
fs::copyArgsDestroy(c);
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::dumpAllUsersAllSaves(void *a) {
|
||||||
|
threadInfo *t = (threadInfo *) a;
|
||||||
|
fs::copyArgs *c = fs::copyArgsCreate("", "", "", NULL, NULL, false, false, 0);
|
||||||
|
t->argPtr = c;
|
||||||
|
unsigned curUser = 0;
|
||||||
|
while (data::users[curUser].getUID128() != 2) {
|
||||||
|
data::user *u = &data::users[curUser++];
|
||||||
|
for (unsigned i = 0; i < u->titleInfo.size(); i++) {
|
||||||
|
bool saveMounted = fs::mountSave(u->titleInfo[i].saveInfo);
|
||||||
|
util::createTitleDirectoryByTID(u->titleInfo[i].tid);
|
||||||
|
if (saveMounted && fs::dirNotEmpty("sv:/")) {
|
||||||
|
fs::loadPathFilters(u->titleInfo[i].tid);
|
||||||
|
std::string dst = util::generatePathByTID(u->titleInfo[i].tid) + u->getUsernameSafe() + " - " +
|
||||||
|
util::getDateTime(util::DATE_FMT_YMD) + "/";
|
||||||
|
fs::mkDir(dst.substr(0, dst.length() - 1));
|
||||||
|
fs::copyDirToDir("sv:/", dst, t);
|
||||||
|
fs::freePathFilters();
|
||||||
|
}
|
||||||
|
fs::unmountSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs::copyArgsDestroy(c);
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::logOpen() {
|
||||||
|
std::string logPath = wd + "log.txt";
|
||||||
|
debLog = fsfopen(logPath.c_str(), FsOpenMode_Write);
|
||||||
|
fsfclose(debLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::logWrite(const char *fmt, ...) {
|
||||||
|
std::string logPath = wd + "log.txt";
|
||||||
|
debLog = fsfopen(logPath.c_str(), FsOpenMode_Append | FsOpenMode_Write);
|
||||||
|
char tmp[256];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsprintf(tmp, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
fsfwrite(tmp, 1, strlen(tmp), debLog);
|
||||||
|
fsfclose(debLog);
|
||||||
|
}
|
||||||
|
|
||||||
266
source/fs/dir.cpp
Normal file
266
source/fs/dir.cpp
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "fs.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
bool operator()(const fs::dirItem& a, const fs::dirItem& b)
|
||||||
|
{
|
||||||
|
if(a.isDir() != b.isDir())
|
||||||
|
return a.isDir();
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < a.getItm().length(); i++)
|
||||||
|
{
|
||||||
|
char charA = tolower(a.getItm()[i]);
|
||||||
|
char charB = tolower(b.getItm()[i]);
|
||||||
|
if(charA != charB)
|
||||||
|
return charA < charB;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} sortDirList;
|
||||||
|
|
||||||
|
void fs::mkDir(const std::string& _p)
|
||||||
|
{
|
||||||
|
mkdir(_p.c_str(), 777);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::mkDirRec(const std::string& _p)
|
||||||
|
{
|
||||||
|
//skip first slash
|
||||||
|
size_t pos = _p.find('/', 0) + 1;
|
||||||
|
while((pos = _p.find('/', pos)) != _p.npos)
|
||||||
|
{
|
||||||
|
fs::mkDir(_p.substr(0, pos).c_str());
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::delDir(const std::string& path)
|
||||||
|
{
|
||||||
|
dirList list(path);
|
||||||
|
for(unsigned i = 0; i < list.getCount(); i++)
|
||||||
|
{
|
||||||
|
if(pathIsFiltered(path + list.getItem(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list.isDir(i))
|
||||||
|
{
|
||||||
|
std::string newPath = path + list.getItem(i) + "/";
|
||||||
|
delDir(newPath);
|
||||||
|
|
||||||
|
std::string delPath = path + list.getItem(i);
|
||||||
|
// rmdir(delPath.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string delPath = path + list.getItem(i);
|
||||||
|
std::remove(delPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// rmdir(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::dirNotEmpty(const std::string& _dir)
|
||||||
|
{
|
||||||
|
fs::dirList tmp(_dir);
|
||||||
|
return tmp.getCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::isDir(const std::string& _path)
|
||||||
|
{
|
||||||
|
struct stat s;
|
||||||
|
return stat(_path.c_str(), &s) == 0 && S_ISDIR(s.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToDir(const std::string& src, const std::string& dst, threadInfo *t)
|
||||||
|
{
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("Opening '#%s#'...");
|
||||||
|
|
||||||
|
fs::dirList *list = new fs::dirList(src);
|
||||||
|
for(unsigned i = 0; i < list->getCount(); i++)
|
||||||
|
{
|
||||||
|
if(pathIsFiltered(src + list->getItem(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list->isDir(i))
|
||||||
|
{
|
||||||
|
std::string newSrc = src + list->getItem(i) + "/";
|
||||||
|
std::string newDst = dst + list->getItem(i) + "/";
|
||||||
|
fs::mkDir(newDst.substr(0, newDst.length() - 1));
|
||||||
|
fs::copyDirToDir(newSrc, newDst, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string fullSrc = src + list->getItem(i);
|
||||||
|
std::string fullDst = dst + list->getItem(i);
|
||||||
|
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("Copying '#%s#'...");
|
||||||
|
|
||||||
|
fs::copyFile(fullSrc, fullDst, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyDirToDir_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
|
||||||
|
fs::copyDirToDir(in->src, in->dst, t);
|
||||||
|
if(in->cleanup)
|
||||||
|
fs::copyArgsDestroy(in);
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToDirThreaded(const std::string& src, const std::string& dst)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate(src, dst, "", NULL, NULL, true, false, 0);
|
||||||
|
threads::newThread(copyDirToDir_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToDirCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t)
|
||||||
|
{
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("Opening '#%s#'...");
|
||||||
|
|
||||||
|
fs::dirList *list = new fs::dirList(src);
|
||||||
|
for(unsigned i = 0; i < list->getCount(); i++)
|
||||||
|
{
|
||||||
|
if(pathIsFiltered(src + list->getItem(i)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list->isDir(i))
|
||||||
|
{
|
||||||
|
std::string newSrc = src + list->getItem(i) + "/";
|
||||||
|
std::string newDst = dst + list->getItem(i) + "/";
|
||||||
|
fs::mkDir(newDst.substr(0, newDst.length() - 1));
|
||||||
|
fs::copyDirToDirCommit(newSrc, newDst, dev, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string fullSrc = src + list->getItem(i);
|
||||||
|
std::string fullDst = dst + list->getItem(i);
|
||||||
|
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("Copying '#%s#'...");
|
||||||
|
|
||||||
|
fs::copyFileCommit(fullSrc, fullDst, dev, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyDirToDirCommit_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
|
||||||
|
fs::copyDirToDirCommit(in->src, in->dst, in->dev, t);
|
||||||
|
if(in->cleanup)
|
||||||
|
fs::copyArgsDestroy(in);
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToDirCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate(src, dst, dev, NULL, NULL, true, false, 0);
|
||||||
|
threads::newThread(copyDirToDirCommit_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::getDirProps(const std::string& path, unsigned& dirCount, unsigned& fileCount, uint64_t& totalSize)
|
||||||
|
{
|
||||||
|
fs::dirList *d = new fs::dirList(path);
|
||||||
|
for(unsigned i = 0; i < d->getCount(); i++)
|
||||||
|
{
|
||||||
|
if(d->isDir(i))
|
||||||
|
{
|
||||||
|
++dirCount;
|
||||||
|
std::string newPath = path + d->getItem(i) + "/";
|
||||||
|
fs::getDirProps(newPath, dirCount, fileCount, totalSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++fileCount;
|
||||||
|
std::string filePath = path + d->getItem(i);
|
||||||
|
totalSize += fs::fsize(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::dirItem::dirItem(const std::string& pathTo, const std::string& sItem)
|
||||||
|
{
|
||||||
|
itm = sItem;
|
||||||
|
|
||||||
|
std::string fullPath = pathTo + sItem;
|
||||||
|
struct stat s;
|
||||||
|
if(stat(fullPath.c_str(), &s) == 0 && S_ISDIR(s.st_mode))
|
||||||
|
dir = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fs::dirItem::getName() const
|
||||||
|
{
|
||||||
|
size_t extPos = itm.find_last_of('.'), slPos = itm.find_last_of('/');
|
||||||
|
if(extPos == itm.npos)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return itm.substr(slPos + 1, extPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fs::dirItem::getExt() const
|
||||||
|
{
|
||||||
|
return util::getExtensionFromString(itm);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::dirList::dirList(const std::string& _path)
|
||||||
|
{
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *ent;
|
||||||
|
path = _path;
|
||||||
|
d = opendir(path.c_str());
|
||||||
|
|
||||||
|
while((ent = readdir(d)))
|
||||||
|
item.emplace_back(path, ent->d_name);
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
|
||||||
|
std::sort(item.begin(), item.end(), sortDirList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::dirList::reassign(const std::string& _path)
|
||||||
|
{
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *ent;
|
||||||
|
path = _path;
|
||||||
|
|
||||||
|
d = opendir(path.c_str());
|
||||||
|
|
||||||
|
item.clear();
|
||||||
|
|
||||||
|
while((ent = readdir(d)))
|
||||||
|
item.emplace_back(path, ent->d_name);
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
|
||||||
|
std::sort(item.begin(), item.end(), sortDirList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::dirList::rescan()
|
||||||
|
{
|
||||||
|
item.clear();
|
||||||
|
DIR *d;
|
||||||
|
struct dirent *ent;
|
||||||
|
d = opendir(path.c_str());
|
||||||
|
|
||||||
|
while((ent = readdir(d)))
|
||||||
|
item.emplace_back(path, ent->d_name);
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
|
||||||
|
std::sort(item.begin(), item.end(), sortDirList);
|
||||||
|
}
|
||||||
383
source/fs/file.cpp
Normal file
383
source/fs/file.cpp
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
#include <switch.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fs.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
|
static std::string wd = "sdmc:/JKSV/";
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
std::mutex bufferLock;
|
||||||
|
std::condition_variable cond;
|
||||||
|
std::vector<uint8_t> sharedBuffer;
|
||||||
|
std::string dst, dev;
|
||||||
|
bool bufferIsFull = false;
|
||||||
|
unsigned int filesize = 0, writeLimit = 0;
|
||||||
|
} fileCpyThreadArgs;
|
||||||
|
|
||||||
|
static void writeFile_t(void *a)
|
||||||
|
{
|
||||||
|
fileCpyThreadArgs *in = (fileCpyThreadArgs *)a;
|
||||||
|
size_t written = 0;
|
||||||
|
std::vector<uint8_t> localBuffer;
|
||||||
|
std::FILE *out = std::fopen(in->dst.c_str(), "wb");
|
||||||
|
|
||||||
|
while(written < in->filesize)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> buffLock(in->bufferLock);
|
||||||
|
in->cond.wait(buffLock, [in]{ return in->bufferIsFull;});
|
||||||
|
localBuffer.clear();
|
||||||
|
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||||
|
in->sharedBuffer.clear();
|
||||||
|
in->bufferIsFull = false;
|
||||||
|
buffLock.unlock();
|
||||||
|
in->cond.notify_one();
|
||||||
|
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeFileCommit_t(void *a)
|
||||||
|
{
|
||||||
|
fileCpyThreadArgs *in = (fileCpyThreadArgs *)a;
|
||||||
|
size_t written = 0, journalCount = 0;
|
||||||
|
std::vector<uint8_t> localBuffer;
|
||||||
|
FILE *out = fopen(in->dst.c_str(), "wb");
|
||||||
|
|
||||||
|
while(written < in->filesize)
|
||||||
|
{`
|
||||||
|
std::unique_lock<std::mutex> buffLock(in->bufferLock);
|
||||||
|
in->cond.wait(buffLock, [in]{ return in->bufferIsFull; });
|
||||||
|
localBuffer.clear();
|
||||||
|
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||||
|
in->sharedBuffer.clear();
|
||||||
|
in->bufferIsFull = false;
|
||||||
|
buffLock.unlock();
|
||||||
|
in->cond.notify_one();
|
||||||
|
|
||||||
|
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
|
||||||
|
|
||||||
|
journalCount += written;
|
||||||
|
if(journalCount >= in->writeLimit)
|
||||||
|
{
|
||||||
|
journalCount = 0;
|
||||||
|
fclose(out);
|
||||||
|
fs::commitToDevice(in->dev.c_str());
|
||||||
|
out = fopen(in->dst.c_str(), "ab");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::copyArgs *fs::copyArgsCreate(const std::string& src, const std::string& dst, const std::string& dev, zipFile z, unzFile unz, bool _cleanup, bool _trimZipPath, uint8_t _trimPlaces)
|
||||||
|
{
|
||||||
|
copyArgs *ret = new copyArgs;
|
||||||
|
ret->src = src;
|
||||||
|
ret->dst = dst;
|
||||||
|
ret->dev = dev;
|
||||||
|
ret->z = z;
|
||||||
|
ret->unz = unz;
|
||||||
|
ret->cleanup = _cleanup;
|
||||||
|
ret->offset = 0;
|
||||||
|
ret->trimZipPath = _trimZipPath;
|
||||||
|
ret->trimZipPlaces = _trimPlaces;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyArgsDestroy(copyArgs *c)
|
||||||
|
{
|
||||||
|
// delete c->prog;
|
||||||
|
delete c;
|
||||||
|
c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::dataFile::dataFile(const std::string& _path)
|
||||||
|
{
|
||||||
|
f = fopen(_path.c_str(), "r");
|
||||||
|
if(f != NULL)
|
||||||
|
opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::dataFile::~dataFile()
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::dataFile::readNextLine(bool proc)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
char tmp[1024];
|
||||||
|
while(fgets(tmp, 1024, f))
|
||||||
|
{
|
||||||
|
if(tmp[0] != '#' && tmp[0] != '\n' && tmp[0] != '\r')
|
||||||
|
{
|
||||||
|
line = tmp;
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
util::stripChar('\n', line);
|
||||||
|
util::stripChar('\r', line);
|
||||||
|
if(proc)
|
||||||
|
procLine();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::dataFile::procLine()
|
||||||
|
{
|
||||||
|
size_t pPos = line.find_first_of("(=,");
|
||||||
|
if(pPos != line.npos)
|
||||||
|
{
|
||||||
|
lPos = pPos;
|
||||||
|
name.assign(line.begin(), line.begin() + lPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
name = line;
|
||||||
|
|
||||||
|
util::stripChar(' ', name);
|
||||||
|
++lPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fs::dataFile::getNextValueStr()
|
||||||
|
{
|
||||||
|
std::string ret = "";
|
||||||
|
//Skip all spaces until we hit actual text
|
||||||
|
size_t pos1 = line.find_first_not_of(", ", lPos);
|
||||||
|
//If reading from quotes
|
||||||
|
if(line[pos1] == '"')
|
||||||
|
lPos = line.find_first_of('"', ++pos1);
|
||||||
|
else
|
||||||
|
lPos = line.find_first_of(",;\n", pos1);//Set lPos to end of string we want. This should just set lPos to the end of the line if it fails, which is ok
|
||||||
|
|
||||||
|
ret = line.substr(pos1, lPos++ - pos1);
|
||||||
|
|
||||||
|
util::replaceStr(ret, "\\n", "\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fs::dataFile::getNextValueInt()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
std::string no = getNextValueStr();
|
||||||
|
if(no[0] == '0' && tolower(no[1]) == 'x')
|
||||||
|
ret = strtoul(no.c_str(), NULL, 16);
|
||||||
|
else
|
||||||
|
ret = strtoul(no.c_str(), NULL, 10);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyFile(const std::string& src, const std::string& dst, threadInfo *t)
|
||||||
|
{
|
||||||
|
fs::copyArgs *c = NULL;
|
||||||
|
size_t filesize = fs::fsize(src);
|
||||||
|
if(t)
|
||||||
|
{
|
||||||
|
c = (fs::copyArgs *)t->argPtr;
|
||||||
|
c->offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fsrc = fopen(src.c_str(), "rb");
|
||||||
|
if(!fsrc)
|
||||||
|
{
|
||||||
|
fclose(fsrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileCpyThreadArgs thrdArgs;
|
||||||
|
thrdArgs.dst = dst;
|
||||||
|
thrdArgs.filesize = filesize;
|
||||||
|
|
||||||
|
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||||
|
std::vector<uint8_t> transferBuffer;
|
||||||
|
Thread writeThread;
|
||||||
|
threadCreate(&writeThread, writeFile_t, &thrdArgs, NULL, 0x40000, 0x2E, 2);
|
||||||
|
threadStart(&writeThread);
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint64_t readCount = 0;
|
||||||
|
while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0)
|
||||||
|
{
|
||||||
|
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
|
||||||
|
readCount += readIn;
|
||||||
|
|
||||||
|
if(c)
|
||||||
|
c->offset = readCount;
|
||||||
|
|
||||||
|
if(transferBuffer.size() >= TRANSFER_BUFFER_LIMIT || readCount == filesize)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
|
||||||
|
thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; });
|
||||||
|
thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
|
||||||
|
transferBuffer.clear();
|
||||||
|
thrdArgs.bufferIsFull = true;
|
||||||
|
buffLock.unlock();
|
||||||
|
thrdArgs.cond.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadWaitForExit(&writeThread);
|
||||||
|
threadClose(&writeThread);
|
||||||
|
fclose(fsrc);
|
||||||
|
delete[] buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void copyFileThreaded_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
|
||||||
|
|
||||||
|
t->status->setStatus("Copy file", in->src.c_str());
|
||||||
|
|
||||||
|
fs::copyFile(in->src, in->dst, t);
|
||||||
|
if(in->cleanup)
|
||||||
|
fs::copyArgsDestroy(in);
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyFileThreaded(const std::string& src, const std::string& dst)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate(src, dst, "", NULL, NULL, true, false, 0);
|
||||||
|
threads::newThread(copyFileThreaded_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyFileCommit(const std::string& src, const std::string& dst, const std::string& dev, threadInfo *t)
|
||||||
|
{
|
||||||
|
fs::copyArgs *c = NULL;
|
||||||
|
size_t filesize = fs::fsize(src);
|
||||||
|
if(t)
|
||||||
|
{
|
||||||
|
c = (fs::copyArgs *)t->argPtr;
|
||||||
|
c->offset = 0;
|
||||||
|
// c->prog->setMax(filesize);
|
||||||
|
// c->prog->update(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fsrc = fopen(src.c_str(), "rb");
|
||||||
|
if(!fsrc)
|
||||||
|
{
|
||||||
|
fclose(fsrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileCpyThreadArgs thrdArgs;
|
||||||
|
thrdArgs.dst = dst;
|
||||||
|
thrdArgs.dev = dev;
|
||||||
|
thrdArgs.filesize = filesize;
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
uint64_t journalSpace = fs::getJournalSize(utinfo);
|
||||||
|
thrdArgs.writeLimit = (journalSpace - 0x100000) < TRANSFER_BUFFER_LIMIT ? journalSpace - 0x100000 : TRANSFER_BUFFER_LIMIT;
|
||||||
|
|
||||||
|
Thread writeThread;
|
||||||
|
threadCreate(&writeThread, writeFileCommit_t, &thrdArgs, NULL, 0x040000, 0x2E, 2);
|
||||||
|
|
||||||
|
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint64_t readCount = 0;
|
||||||
|
std::vector<uint8_t> transferBuffer;
|
||||||
|
threadStart(&writeThread);
|
||||||
|
while((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0)
|
||||||
|
{
|
||||||
|
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
|
||||||
|
readCount += readIn;
|
||||||
|
if(c)
|
||||||
|
c->offset = readCount;
|
||||||
|
|
||||||
|
if(transferBuffer.size() >= thrdArgs.writeLimit || readCount == filesize)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
|
||||||
|
thrdArgs.cond.wait(buffLock, [&thrdArgs]{ return thrdArgs.bufferIsFull == false; });
|
||||||
|
thrdArgs.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
|
||||||
|
transferBuffer.clear();
|
||||||
|
thrdArgs.bufferIsFull = true;
|
||||||
|
buffLock.unlock();
|
||||||
|
thrdArgs.cond.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadWaitForExit(&writeThread);
|
||||||
|
threadClose(&writeThread);
|
||||||
|
|
||||||
|
fclose(fsrc);
|
||||||
|
fs::commitToDevice(dev);
|
||||||
|
delete[] buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyFileCommit_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *in = (fs::copyArgs *)t->argPtr;
|
||||||
|
|
||||||
|
t->status->setStatus("Copy file", in->src.c_str());
|
||||||
|
|
||||||
|
fs::copyFileCommit(in->src, in->dst, in->dev, t);
|
||||||
|
if(in->cleanup)
|
||||||
|
fs::copyArgsDestroy(in);
|
||||||
|
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyFileCommitThreaded(const std::string& src, const std::string& dst, const std::string& dev)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate(src, dst, dev, NULL, NULL, true, false, 0);
|
||||||
|
threads::newThread(copyFileCommit_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::fileDrawFunc(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
if(!t->finished && t->argPtr)
|
||||||
|
{
|
||||||
|
copyArgs *c = (copyArgs *)t->argPtr;
|
||||||
|
std::string tmp;
|
||||||
|
t->status->getStatus(tmp);
|
||||||
|
c->argLock();
|
||||||
|
// c->prog->update(c->offset);
|
||||||
|
// c->prog->draw(tmp);
|
||||||
|
c->argUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::delfile(const std::string& path)
|
||||||
|
{
|
||||||
|
remove(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::getShowFileProps(const std::string& _path)
|
||||||
|
{
|
||||||
|
size_t size = fs::fsize(_path);
|
||||||
|
// ui::showMessage(ui::getUICString("fileModeFileProperties", 0), _path.c_str(), util::getSizeString(size).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::fileExists(const std::string& path)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
FILE *test = fopen(path.c_str(), "rb");
|
||||||
|
if(test != NULL)
|
||||||
|
ret = true;
|
||||||
|
fclose(test);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fs::fsize(const std::string& _f)
|
||||||
|
{
|
||||||
|
size_t ret = 0;
|
||||||
|
FILE *get = fopen(_f.c_str(), "rb");
|
||||||
|
if(get != NULL)
|
||||||
|
{
|
||||||
|
fseek(get, 0, SEEK_END);
|
||||||
|
ret = ftell(get);
|
||||||
|
}
|
||||||
|
fclose(get);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
150
source/fs/fsfile.c
Normal file
150
source/fs/fsfile.c
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "fs/fsfile.h"
|
||||||
|
|
||||||
|
char *getDeviceFromPath(char *dev, size_t _max, const char *path)
|
||||||
|
{
|
||||||
|
memset(dev, 0, _max);
|
||||||
|
char *c = strchr(path, ':');
|
||||||
|
if(c - path > _max)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
//probably not good? idk
|
||||||
|
memcpy(dev, path, c - path);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *getFilePath(char *pathOut, size_t _max, const char *path)
|
||||||
|
{
|
||||||
|
memset(pathOut, 0, _max);
|
||||||
|
char *c = strchr(path, '/');
|
||||||
|
size_t pLength = strlen(c);
|
||||||
|
if(pLength > _max)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(pathOut, c, pLength);
|
||||||
|
|
||||||
|
return pathOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fsMkDir(const char *_p)
|
||||||
|
{
|
||||||
|
char devStr[16];
|
||||||
|
char path[FS_MAX_PATH];
|
||||||
|
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(path, FS_MAX_PATH, _p))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Result res = fsFsCreateDirectory(fsdevGetDeviceFileSystem(devStr), path);
|
||||||
|
return res == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fsremove(const char *_p)
|
||||||
|
{
|
||||||
|
char devStr[16];
|
||||||
|
char path[FS_MAX_PATH];
|
||||||
|
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(path, FS_MAX_PATH, _p))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
Result res = fsFsDeleteFile(fsdevGetDeviceFileSystem(devStr), path);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result fsDelDirRec(const char *_p)
|
||||||
|
{
|
||||||
|
char devStr[16];
|
||||||
|
char path[FS_MAX_PATH];
|
||||||
|
if(!getDeviceFromPath(devStr, 16, _p) || ! getFilePath(path, FS_MAX_PATH, _p))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return fsFsDeleteDirectoryRecursively(fsdevGetDeviceFileSystem(devStr), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fsfcreate(const char *_p, int64_t crSize)
|
||||||
|
{
|
||||||
|
char devStr[16];
|
||||||
|
char filePath[FS_MAX_PATH];
|
||||||
|
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(filePath, FS_MAX_PATH, _p))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FsFileSystem *s = fsdevGetDeviceFileSystem(devStr);
|
||||||
|
if(s == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Result res = fsFsCreateFile(s, filePath, crSize, 0);
|
||||||
|
if(R_SUCCEEDED(res))
|
||||||
|
res = fsdevCommitDevice(devStr);
|
||||||
|
|
||||||
|
return R_SUCCEEDED(res) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSFILE *fsfopen(const char *_p, uint32_t mode)
|
||||||
|
{
|
||||||
|
char devStr[16];
|
||||||
|
char filePath[FS_MAX_PATH];
|
||||||
|
if(!getDeviceFromPath(devStr, 16, _p) || !getFilePath(filePath, FS_MAX_PATH, _p))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FsFileSystem *s = fsdevGetDeviceFileSystem(devStr);
|
||||||
|
if(s == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(mode == FsOpenMode_Write)
|
||||||
|
{
|
||||||
|
fsFsDeleteFile(s, filePath);
|
||||||
|
fsFsCreateFile(s, filePath, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSFILE *ret = malloc(sizeof(FSFILE));
|
||||||
|
ret->error = fsFsOpenFile(s, filePath, mode, &ret->_f);
|
||||||
|
if(R_FAILED(ret->error))
|
||||||
|
{
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fsFileGetSize(&ret->_f, &ret->fsize);
|
||||||
|
ret->offset = (mode & FsOpenMode_Append) ? ret->fsize : 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSFILE *fsfopenWithSystem(FsFileSystem *_s, const char *_p, uint32_t mode)
|
||||||
|
{
|
||||||
|
if(mode & FsOpenMode_Write)
|
||||||
|
{
|
||||||
|
fsFsDeleteFile(_s, _p);
|
||||||
|
fsFsCreateFile(_s, _p, 0, 0);
|
||||||
|
}
|
||||||
|
else if(mode & FsOpenMode_Append)
|
||||||
|
fsFsCreateFile(_s, _p, 0, 0);
|
||||||
|
|
||||||
|
FSFILE *ret = malloc(sizeof(FSFILE));
|
||||||
|
ret->error = fsFsOpenFile(_s, _p, mode, &ret->_f);
|
||||||
|
if(R_FAILED(ret->error))
|
||||||
|
{
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fsFileGetSize(&ret->_f, &ret->fsize);
|
||||||
|
ret->offset = (mode & FsOpenMode_Append) ? ret->fsize : 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fsfwrite(const void *buf, size_t sz, size_t count, FSFILE *_f)
|
||||||
|
{
|
||||||
|
size_t fullSize = sz * count;
|
||||||
|
if(_f->offset + fullSize > _f->fsize)
|
||||||
|
{
|
||||||
|
s64 newSize = (_f->fsize + fullSize) - (_f->fsize - _f->offset);
|
||||||
|
fsFileSetSize(&_f->_f, newSize);
|
||||||
|
_f->fsize = newSize;
|
||||||
|
}
|
||||||
|
_f->error = fsFileWrite(&_f->_f, _f->offset, buf, fullSize, FsWriteOption_Flush);
|
||||||
|
_f->offset += fullSize;
|
||||||
|
|
||||||
|
return fullSize;
|
||||||
|
}
|
||||||
251
source/fs/zip.cpp
Normal file
251
source/fs/zip.cpp
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#include <switch.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include "fs.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "threads.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
std::mutex buffLock;
|
||||||
|
std::condition_variable cond;
|
||||||
|
std::vector<uint8_t> sharedBuffer;
|
||||||
|
std::string dst, dev;
|
||||||
|
bool bufferIsFull = false;
|
||||||
|
unzFile unz;
|
||||||
|
unsigned int fileSize, writeLimit = 0;
|
||||||
|
} unzThrdArgs;
|
||||||
|
|
||||||
|
static void writeFileFromZip_t(void *a)
|
||||||
|
{
|
||||||
|
unzThrdArgs *in = (unzThrdArgs *)a;
|
||||||
|
std::vector<uint8_t> localBuffer;
|
||||||
|
unsigned int written = 0, journalCount = 0;
|
||||||
|
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
uint64_t journalSpace = fs::getJournalSize(utinfo);
|
||||||
|
|
||||||
|
FILE *out = fopen(in->dst.c_str(), "wb");
|
||||||
|
while(written < in->fileSize)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> buffLock(in->buffLock);
|
||||||
|
in->cond.wait(buffLock, [in]{ return in->bufferIsFull; });
|
||||||
|
localBuffer.clear();
|
||||||
|
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||||
|
in->sharedBuffer.clear();
|
||||||
|
in->bufferIsFull = false;
|
||||||
|
buffLock.unlock();
|
||||||
|
in->cond.notify_one();
|
||||||
|
|
||||||
|
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
|
||||||
|
journalCount += written;
|
||||||
|
if(journalCount >= in->writeLimit)
|
||||||
|
{
|
||||||
|
journalCount = 0;
|
||||||
|
fclose(out);
|
||||||
|
fs::commitToDevice(in->dev);
|
||||||
|
out = fopen(in->dst.c_str(), "ab");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToZip(const std::string& src, zipFile dst, bool trimPath, int trimPlaces, threadInfo *t)
|
||||||
|
{
|
||||||
|
fs::copyArgs *c = NULL;
|
||||||
|
if(t)
|
||||||
|
{
|
||||||
|
t->status->setStatus("threadStatusOpeningFolder");
|
||||||
|
c = (fs::copyArgs *)t->argPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::dirList *list = new fs::dirList(src);
|
||||||
|
for(unsigned i = 0; i < list->getCount(); i++)
|
||||||
|
{
|
||||||
|
std::string itm = list->getItem(i);
|
||||||
|
if(fs::pathIsFiltered(src + itm))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(list->isDir(i))
|
||||||
|
{
|
||||||
|
std::string newSrc = src + itm + "/";
|
||||||
|
fs::copyDirToZip(newSrc, dst, trimPath, trimPlaces, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
time_t raw;
|
||||||
|
time(&raw);
|
||||||
|
tm *locTime = localtime(&raw);
|
||||||
|
zip_fileinfo inf = { (unsigned)locTime->tm_sec, (unsigned)locTime->tm_min, (unsigned)locTime->tm_hour,
|
||||||
|
(unsigned)locTime->tm_mday, (unsigned)locTime->tm_mon, (unsigned)(1900 + locTime->tm_year), 0, 0, 0 };
|
||||||
|
|
||||||
|
std::string filename = src + itm;
|
||||||
|
size_t zipNameStart = 0;
|
||||||
|
if(trimPath)
|
||||||
|
util::trimPath(filename, trimPlaces);
|
||||||
|
else
|
||||||
|
zipNameStart = filename.find_first_of('/') + 1;
|
||||||
|
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("threadStatusAddingFileToZip");
|
||||||
|
|
||||||
|
int zipOpenFile = zipOpenNewFileInZip64(dst, filename.substr(zipNameStart, filename.npos).c_str(), &inf, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0);
|
||||||
|
if(zipOpenFile == ZIP_OK)
|
||||||
|
{
|
||||||
|
std::string fullSrc = src + itm;
|
||||||
|
if(c)
|
||||||
|
{
|
||||||
|
c->offset = 0;
|
||||||
|
// c->prog->setMax(fs::fsize(fullSrc));
|
||||||
|
// c->prog->update(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fsrc = fopen(fullSrc.c_str(), "rb");
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint8_t *buff = new uint8_t[ZIP_BUFF_SIZE];
|
||||||
|
while((readIn = fread(buff, 1, ZIP_BUFF_SIZE, fsrc)) > 0)
|
||||||
|
{
|
||||||
|
zipWriteInFileInZip(dst, buff, readIn);
|
||||||
|
if(c)
|
||||||
|
c->offset += readIn;
|
||||||
|
}
|
||||||
|
delete[] buff;
|
||||||
|
fclose(fsrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyDirToZip_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
|
||||||
|
|
||||||
|
fs::copyDirToZip(c->src, c->z, c->trimZipPath, c->trimZipPlaces, t);
|
||||||
|
|
||||||
|
if(c->cleanup)
|
||||||
|
{
|
||||||
|
zipClose(c->z, NULL);
|
||||||
|
delete c;
|
||||||
|
}
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyDirToZipThreaded(const std::string& src, zipFile dst, bool trimPath, int trimPlaces)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate(src, "", "", dst, NULL, true, false, 0);
|
||||||
|
threads::newThread(copyDirToZip_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyZipToDir(unzFile src, const std::string& dst, const std::string& dev, threadInfo *t)
|
||||||
|
{
|
||||||
|
fs::copyArgs *c = NULL;
|
||||||
|
if(t)
|
||||||
|
c = (fs::copyArgs *)t->argPtr;
|
||||||
|
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
uint64_t journalSize = getJournalSize(utinfo), writeCount = 0;
|
||||||
|
char filename[FS_MAX_PATH];
|
||||||
|
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||||
|
int readIn = 0;
|
||||||
|
unz_file_info64 info;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
unzGetCurrentFileInfo64(src, &info, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
|
||||||
|
if(unzOpenCurrentFile(src) == UNZ_OK)
|
||||||
|
{
|
||||||
|
if(t)
|
||||||
|
t->status->setStatus("threadStatusDecompressingFile");
|
||||||
|
|
||||||
|
if(c)
|
||||||
|
{
|
||||||
|
// c->prog->setMax(info.uncompressed_size);
|
||||||
|
// c->prog->update(0);
|
||||||
|
c->offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fullDst = dst + filename;
|
||||||
|
fs::mkDirRec(fullDst.substr(0, fullDst.find_last_of('/') + 1));
|
||||||
|
|
||||||
|
unzThrdArgs unzThrd;
|
||||||
|
unzThrd.dst = fullDst;
|
||||||
|
unzThrd.fileSize = info.uncompressed_size;
|
||||||
|
unzThrd.dev = dev;
|
||||||
|
unzThrd.writeLimit = (journalSize - 0x100000) < TRANSFER_BUFFER_LIMIT ? (journalSize - 0x100000) : TRANSFER_BUFFER_LIMIT;
|
||||||
|
|
||||||
|
Thread writeThread;
|
||||||
|
threadCreate(&writeThread, writeFileFromZip_t, &unzThrd, NULL, 0x8000, 0x2B, 2);
|
||||||
|
threadStart(&writeThread);
|
||||||
|
|
||||||
|
std::vector<uint8_t> transferBuffer;
|
||||||
|
uint64_t readCount = 0;
|
||||||
|
while((readIn = unzReadCurrentFile(src, buff, BUFF_SIZE)) > 0)
|
||||||
|
{
|
||||||
|
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
|
||||||
|
readCount += readIn;
|
||||||
|
|
||||||
|
if(c)
|
||||||
|
c->offset += readIn;
|
||||||
|
|
||||||
|
if(transferBuffer.size() >= unzThrd.writeLimit || readCount == info.uncompressed_size)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> buffLock(unzThrd.buffLock);
|
||||||
|
unzThrd.cond.wait(buffLock, [&unzThrd]{ return unzThrd.bufferIsFull == false; });
|
||||||
|
unzThrd.sharedBuffer.assign(transferBuffer.begin(), transferBuffer.end());
|
||||||
|
transferBuffer.clear();
|
||||||
|
unzThrd.bufferIsFull = true;
|
||||||
|
unzThrd.cond.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadWaitForExit(&writeThread);
|
||||||
|
threadClose(&writeThread);
|
||||||
|
fs::commitToDevice(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(unzGoToNextFile(src) != UNZ_END_OF_LIST_OF_FILE);
|
||||||
|
delete[] buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyZipToDir_t(void *a)
|
||||||
|
{
|
||||||
|
threadInfo *t = (threadInfo *)a;
|
||||||
|
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
|
||||||
|
fs::copyZipToDir(c->unz, c->dst, c->dev, t);
|
||||||
|
if(c->cleanup)
|
||||||
|
{
|
||||||
|
unzClose(c->unz);
|
||||||
|
delete c;
|
||||||
|
}
|
||||||
|
t->finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs::copyZipToDirThreaded(unzFile src, const std::string& dst, const std::string& dev)
|
||||||
|
{
|
||||||
|
fs::copyArgs *send = fs::copyArgsCreate("", dst, dev, NULL, src, true, false, 0);
|
||||||
|
threads::newThread(copyZipToDir_t, send, fs::fileDrawFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fs::getZipTotalSize(unzFile unz)
|
||||||
|
{
|
||||||
|
uint64_t ret = 0;
|
||||||
|
if(unzGoToFirstFile(unz) == UNZ_OK)
|
||||||
|
{
|
||||||
|
unz_file_info64 finfo;
|
||||||
|
char filename[FS_MAX_PATH];
|
||||||
|
do
|
||||||
|
{
|
||||||
|
unzGetCurrentFileInfo64(unz, &finfo, filename, FS_MAX_PATH, NULL, 0, NULL, 0);
|
||||||
|
ret += finfo.uncompressed_size;
|
||||||
|
} while(unzGoToNextFile(unz) != UNZ_END_OF_LIST_OF_FILE);
|
||||||
|
unzGoToFirstFile(unz);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs::zipNotEmpty(unzFile unz)
|
||||||
|
{
|
||||||
|
return unzGoToFirstFile(unz) == UNZ_OK;
|
||||||
|
}
|
||||||
543
source/ldn.cpp
Normal file
543
source/ldn.cpp
Normal file
@@ -0,0 +1,543 @@
|
|||||||
|
#include "ldn.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "fs.h"
|
||||||
|
#include "file.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static const u8 sec_data[0x10] = {0x04, 0xb9, 0x9d, 0x4d, 0x58, 0xbc,
|
||||||
|
0x65, 0xe1, 0x77, 0x13, 0xc2, 0xb8,
|
||||||
|
0xd1, 0xb8, 0xec, 0xf6};
|
||||||
|
|
||||||
|
Result create_network(const LdnSecurityConfig *sec_config,
|
||||||
|
const LdnUserConfig *user_config,
|
||||||
|
const LdnNetworkConfig *netconfig, const void *advert,
|
||||||
|
size_t advert_size) {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
rc = ldnOpenAccessPoint();
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ldnCreateNetwork(sec_config, user_config, netconfig);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto error_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ldnSetAdvertiseData(advert, advert_size);
|
||||||
|
if (R_FAILED(rc))
|
||||||
|
goto error_close;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
error_close:
|
||||||
|
ldnCloseAccessPoint();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result connect_network(const LdnScanFilter *filter,
|
||||||
|
const LdnSecurityConfig *sec_config,
|
||||||
|
const LdnUserConfig *user_config, const void *advert,
|
||||||
|
size_t advert_size) {
|
||||||
|
Result rc = 0;
|
||||||
|
s32 total_out = 0;
|
||||||
|
LdnNetworkInfo netinfo_list[0x18];
|
||||||
|
|
||||||
|
rc = ldnOpenStation();
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
rc = ldnScan(0, filter, netinfo_list, 0x18, &total_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In an actual app you'd display the output netinfo_list and let the user
|
||||||
|
// select which one to connect to, however in this example we'll just
|
||||||
|
// connect to the first one.
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc) && !total_out) {
|
||||||
|
rc = MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) { // Handle this / parse it with any method you want.
|
||||||
|
if (netinfo_list[0].advertise_data_size != advert_size ||
|
||||||
|
memcmp(netinfo_list[0].advertise_data, advert, advert_size) != 0) {
|
||||||
|
rc = MAKERESULT(Module_Libnx, LibnxError_NotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
rc = ldnConnect(sec_config, user_config, 0, 0, &netinfo_list[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(rc))
|
||||||
|
ldnCloseStation();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void leave_network(void) {
|
||||||
|
Result rc = 0;
|
||||||
|
LdnState state;
|
||||||
|
|
||||||
|
rc = ldnGetState(&state);
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
if (state == LdnState_AccessPointOpened ||
|
||||||
|
state == LdnState_AccessPointCreated) {
|
||||||
|
if (state == LdnState_AccessPointCreated) {
|
||||||
|
rc = ldnDestroyNetwork();
|
||||||
|
}
|
||||||
|
rc = ldnCloseAccessPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == LdnState_StationOpened || state == LdnState_StationConnected) {
|
||||||
|
if (state == LdnState_StationConnected) {
|
||||||
|
rc = ldnDisconnect();
|
||||||
|
}
|
||||||
|
rc = ldnCloseStation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::destroyLDN() {
|
||||||
|
leave_network();
|
||||||
|
ldnExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
LDN::LDNCommunicate* LDN::createCommunicate(void)
|
||||||
|
{
|
||||||
|
LDN::LDNCommunicate* comm = new LDN::LDNCommunicate;
|
||||||
|
comm->serverFD = -1;
|
||||||
|
comm->commFD = -1;
|
||||||
|
return comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::destroyCommunicate(LDNCommunicate *comm) {
|
||||||
|
if (comm->commFD >= 0)
|
||||||
|
close(comm->commFD);
|
||||||
|
if (comm->serverFD >= 0)
|
||||||
|
close(comm->serverFD);
|
||||||
|
delete comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LDN::createLDNServer(LDNCommunicate *comm) {
|
||||||
|
Result rc;
|
||||||
|
LdnSecurityConfig sec_config = {0};
|
||||||
|
LdnUserConfig user_config = {0};
|
||||||
|
LdnNetworkConfig netconfig = {0};
|
||||||
|
LdnState state;
|
||||||
|
data::user *user = data::getCurrentUser();
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
/*
|
||||||
|
* Use Title ID to the advert, so if advert not match
|
||||||
|
* LDN connect between client and server will bind fail
|
||||||
|
*/
|
||||||
|
std::string advertStr = std::to_string(utinfo->tid);
|
||||||
|
int serverSocket;
|
||||||
|
|
||||||
|
// send nickname to make sure
|
||||||
|
strncpy(user_config.nickname, user->getUsername().c_str(), 0x20 - 1);
|
||||||
|
|
||||||
|
netconfig.local_communication_id = -1;
|
||||||
|
netconfig.participant_max = 2; // Adjust as needed.
|
||||||
|
|
||||||
|
sec_config.type = 1;
|
||||||
|
sec_config.data_size = sizeof(sec_data);
|
||||||
|
memcpy(sec_config.data, sec_data, sizeof(sec_data));
|
||||||
|
|
||||||
|
rc = ldnInitialize(LdnServiceType_User);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ldnGetState(&state);
|
||||||
|
if (!R_SUCCEEDED(rc) || state != LdnState_Initialized) {
|
||||||
|
goto ldn_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = create_network(&sec_config, &user_config, &netconfig, advertStr.c_str(),
|
||||||
|
advertStr.length());
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto ldn_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
ldn_out:
|
||||||
|
ldnExit();
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LDN::createLDNClient(LDNCommunicate *comm) {
|
||||||
|
Result rc;
|
||||||
|
LdnUserConfig user_config = {0};
|
||||||
|
LdnSecurityConfig sec_config = {0};
|
||||||
|
LdnNetworkConfig netconfig = {0};
|
||||||
|
LdnScanFilter filter = {0};
|
||||||
|
LdnState state;
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
/*
|
||||||
|
* Use Title ID to the advert, so if advert not match
|
||||||
|
* LDN connect between client and server will bind fail
|
||||||
|
*/
|
||||||
|
std::string advertStr = std::to_string(utinfo->tid);
|
||||||
|
data::user *user = data::getCurrentUser();
|
||||||
|
|
||||||
|
strncpy(user_config.nickname, user->getUsername().c_str(), 0x20 - 1);
|
||||||
|
|
||||||
|
rc = ldnInitialize(LdnServiceType_User);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
netconfig.local_communication_id = -1;
|
||||||
|
netconfig.participant_max = 2; // Adjust as needed.
|
||||||
|
|
||||||
|
sec_config.type = 1;
|
||||||
|
sec_config.data_size = sizeof(sec_data);
|
||||||
|
memcpy(sec_config.data, sec_data, sizeof(sec_data));
|
||||||
|
|
||||||
|
filter.local_communication_id = -1;
|
||||||
|
filter.userdata_filter = netconfig.userdata_filter;
|
||||||
|
filter.flags =
|
||||||
|
LdnScanFilterFlags_LocalCommunicationId | LdnScanFilterFlags_UserData;
|
||||||
|
|
||||||
|
rc = ldnGetState(&state);
|
||||||
|
if (!R_SUCCEEDED(rc) || state != LdnState_Initialized) {
|
||||||
|
goto ldnOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = connect_network(&filter, &sec_config, &user_config, advertStr.c_str(),
|
||||||
|
advertStr.length());
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
ldnOut:
|
||||||
|
ldnExit();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LDN::bindClient(int serverFD) {
|
||||||
|
struct sockaddr_in clientAddr;
|
||||||
|
socklen_t length = sizeof(clientAddr);
|
||||||
|
int cSocket =
|
||||||
|
accept(serverFD, (struct sockaddr *)&clientAddr, &length);
|
||||||
|
if (cSocket < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LDN::bindServer(struct sockaddr_in *serverAddr) {
|
||||||
|
/// sockfd
|
||||||
|
int sock_cli = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
int ret;
|
||||||
|
if ((ret = connect(sock_cli, (struct sockaddr *)serverAddr,
|
||||||
|
sizeof(*serverAddr))) < 0) {
|
||||||
|
close(sock_cli);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sock_cli;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reciveBuf(char *buf, ssize_t size, int socketfd) {
|
||||||
|
ssize_t offset = 0, ssize;
|
||||||
|
while (offset < size) {
|
||||||
|
ssize =
|
||||||
|
size - offset < SOCKET_BUFFER_SIZE ? size - offset : SOCKET_BUFFER_SIZE;
|
||||||
|
ssize_t done = recv(socketfd, buf + offset, ssize, 0);
|
||||||
|
if (done == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset += done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendBuf(const char *buf, ssize_t size, int socketfd) {
|
||||||
|
ssize_t offset = 0, ssize;
|
||||||
|
while (offset < size) {
|
||||||
|
ssize =
|
||||||
|
size - offset < SOCKET_BUFFER_SIZE ? size - offset : SOCKET_BUFFER_SIZE;
|
||||||
|
ssize_t done = send(socketfd, buf + offset, ssize, 0);
|
||||||
|
if (done == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset += done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LDN::waitForOK(int socketfd) {
|
||||||
|
commMeta meta;
|
||||||
|
reciveBuf((char *)&meta, sizeof(meta), socketfd);
|
||||||
|
if (meta.type == UPDATE_OK)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LDN::waitForDONE(int socketfd) {
|
||||||
|
commMeta meta;
|
||||||
|
reciveBuf((char *)&meta, sizeof(meta), socketfd);
|
||||||
|
if (meta.type == UPDATE_DONE)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::sendOK(int socket_fd) {
|
||||||
|
commMeta meta;
|
||||||
|
meta.type = UPDATE_OK;
|
||||||
|
|
||||||
|
sendBuf((char *)&meta, sizeof(meta), socket_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::sendDONE(int socket_fd) {
|
||||||
|
commMeta meta;
|
||||||
|
meta.type = UPDATE_DONE;
|
||||||
|
|
||||||
|
sendBuf((char *)&meta, sizeof(meta), socket_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::sendAbort(int socket_fd) {
|
||||||
|
commMeta meta;
|
||||||
|
meta.type = UPDATE_ABORT;
|
||||||
|
|
||||||
|
sendBuf((char *)&meta, sizeof(meta), socket_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::reciveMeta(commMeta *meta, int socketfd) {
|
||||||
|
bzero(meta, sizeof(commMeta));
|
||||||
|
reciveBuf((char *)meta, sizeof(commMeta), socketfd);
|
||||||
|
sendOK(socketfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::sendMeta(commMeta *meta, int socketfd) {
|
||||||
|
sendBuf((char *)meta, sizeof(commMeta), socketfd);
|
||||||
|
waitForOK(socketfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copySaveFileToRemote_t(void *a) {
|
||||||
|
LDN::LDNfcopyArgs *in = (LDN::LDNfcopyArgs *)a;
|
||||||
|
LDN::LDNCommunicate *comm = in->comm;
|
||||||
|
size_t written = 0;
|
||||||
|
std::vector<uint8_t> localBuffer;
|
||||||
|
|
||||||
|
int fSocket = LDN::bindClient(comm->serverFD);
|
||||||
|
if (fSocket < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (written < in->filesize) {
|
||||||
|
std::unique_lock<std::mutex> buffLock(in->bufferLock);
|
||||||
|
in->cond.wait(buffLock, [in] { return in->bufferIsFull; });
|
||||||
|
localBuffer.clear();
|
||||||
|
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||||
|
in->sharedBuffer.clear();
|
||||||
|
in->bufferIsFull = false;
|
||||||
|
buffLock.unlock();
|
||||||
|
in->cond.notify_one();
|
||||||
|
|
||||||
|
written += send(fSocket, localBuffer.data(), localBuffer.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::copySaveFileToRemote(const std::string &local, threadInfo *t) {
|
||||||
|
fs::copyArgs *c = NULL;
|
||||||
|
c = (fs::copyArgs *)t->argPtr;
|
||||||
|
LDN::LDNCommunicate *comm = c->comm;
|
||||||
|
commMeta cm;
|
||||||
|
std::string zipPath = fs::getWorkDir() + "tempSave.zip";
|
||||||
|
|
||||||
|
//Trim this to unzip direct in direct sv:/
|
||||||
|
int zipTrim = util::getTotalPlacesInPath("sv:/");
|
||||||
|
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
|
||||||
|
|
||||||
|
zipFile zip = zipOpen64(zipPath.c_str(), 0);
|
||||||
|
fs::copyDirToZip(local, zip, true, zipTrim, t);
|
||||||
|
zipClose(zip, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
size_t filesize = fs::fsize(zipPath);
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
c->offset = 0;
|
||||||
|
|
||||||
|
FILE *fsrc = fopen(zipPath.c_str(), "rb");
|
||||||
|
if(!fsrc)
|
||||||
|
{
|
||||||
|
sendAbort(comm->commFD);
|
||||||
|
fclose(fsrc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.type = UPDATE_FILE;
|
||||||
|
cm.fsz = filesize;
|
||||||
|
sendMeta(&cm, comm->commFD);
|
||||||
|
|
||||||
|
LDNfcopyArgs thrdArgs;
|
||||||
|
thrdArgs.comm = comm;
|
||||||
|
thrdArgs.filesize = filesize;
|
||||||
|
|
||||||
|
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||||
|
std::vector<uint8_t> transferBuffer;
|
||||||
|
Thread writeThread;
|
||||||
|
threadCreate(&writeThread, copySaveFileToRemote_t, &thrdArgs, NULL,
|
||||||
|
0x40000, 0x2E, 2);
|
||||||
|
threadStart(&writeThread);
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint64_t readCount = 0;
|
||||||
|
|
||||||
|
while ((readIn = fread(buff, 1, BUFF_SIZE, fsrc)) > 0) {
|
||||||
|
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
|
||||||
|
readCount += readIn;
|
||||||
|
|
||||||
|
if (c)
|
||||||
|
c->offset = readCount;
|
||||||
|
|
||||||
|
if (transferBuffer.size() >= TRANSFER_BUFFER_LIMIT ||
|
||||||
|
readCount == filesize) {
|
||||||
|
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
|
||||||
|
thrdArgs.cond.wait(
|
||||||
|
buffLock, [&thrdArgs] { return thrdArgs.bufferIsFull == false; });
|
||||||
|
thrdArgs.sharedBuffer.assign(transferBuffer.begin(),
|
||||||
|
transferBuffer.end());
|
||||||
|
transferBuffer.clear();
|
||||||
|
thrdArgs.bufferIsFull = true;
|
||||||
|
buffLock.unlock();
|
||||||
|
thrdArgs.cond.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadWaitForExit(&writeThread);
|
||||||
|
threadClose(&writeThread);
|
||||||
|
fclose(fsrc);
|
||||||
|
fs::delfile(zipPath);
|
||||||
|
delete[] buff;
|
||||||
|
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
// wait for client recived sure
|
||||||
|
// otherwise, may lost package
|
||||||
|
LDN::sendDONE(comm->commFD);
|
||||||
|
LDN::waitForDONE(comm->commFD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyRemoteFileWrite_t(void *a) {
|
||||||
|
LDN::LDNfcopyArgs *in = (LDN::LDNfcopyArgs *)a;
|
||||||
|
size_t written = 0;
|
||||||
|
std::vector<uint8_t> localBuffer;
|
||||||
|
FILE *out = fopen(in->dst.c_str(), "wb");
|
||||||
|
|
||||||
|
while (written < in->filesize) {
|
||||||
|
std::unique_lock<std::mutex> buffLock(in->bufferLock);
|
||||||
|
in->cond.wait(buffLock, [in] { return in->bufferIsFull; });
|
||||||
|
localBuffer.clear();
|
||||||
|
localBuffer.assign(in->sharedBuffer.begin(), in->sharedBuffer.end());
|
||||||
|
in->sharedBuffer.clear();
|
||||||
|
in->bufferIsFull = false;
|
||||||
|
buffLock.unlock();
|
||||||
|
in->cond.notify_one();
|
||||||
|
written += fwrite(localBuffer.data(), 1, localBuffer.size(), out);
|
||||||
|
}
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyRemoteSaveFileCommit(LDN::commMeta *meta, threadInfo *t) {
|
||||||
|
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
|
||||||
|
LDN::LDNCommunicate *comm = c->comm;
|
||||||
|
|
||||||
|
std::string fullPath = fs::getWorkDir() + "tempSave.zip";
|
||||||
|
size_t filesize = meta->fsz;
|
||||||
|
|
||||||
|
c->offset = 0;
|
||||||
|
|
||||||
|
int fSocket = LDN::bindServer(&comm->serverAddr);
|
||||||
|
if (fSocket < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
LDN::LDNfcopyArgs thrdArgs;
|
||||||
|
thrdArgs.dst = fullPath;
|
||||||
|
thrdArgs.filesize = filesize;
|
||||||
|
|
||||||
|
uint8_t *buff = new uint8_t[BUFF_SIZE];
|
||||||
|
std::vector<uint8_t> transferBuffer;
|
||||||
|
Thread writeThread;
|
||||||
|
threadCreate(&writeThread, copyRemoteFileWrite_t, &thrdArgs, NULL, 0x40000,
|
||||||
|
0x2E, 2);
|
||||||
|
threadStart(&writeThread);
|
||||||
|
size_t readIn = 0;
|
||||||
|
uint64_t readCount = 0;
|
||||||
|
while ((readIn = recv(fSocket, buff, BUFF_SIZE, 0)) > 0) {
|
||||||
|
transferBuffer.insert(transferBuffer.end(), buff, buff + readIn);
|
||||||
|
readCount += readIn;
|
||||||
|
|
||||||
|
if (c)
|
||||||
|
c->offset = readCount;
|
||||||
|
|
||||||
|
if (transferBuffer.size() >= TRANSFER_BUFFER_LIMIT ||
|
||||||
|
readCount == filesize) {
|
||||||
|
std::unique_lock<std::mutex> buffLock(thrdArgs.bufferLock);
|
||||||
|
thrdArgs.cond.wait(
|
||||||
|
buffLock, [&thrdArgs] { return thrdArgs.bufferIsFull == false; });
|
||||||
|
thrdArgs.sharedBuffer.assign(transferBuffer.begin(),
|
||||||
|
transferBuffer.end());
|
||||||
|
transferBuffer.clear();
|
||||||
|
thrdArgs.bufferIsFull = true;
|
||||||
|
buffLock.unlock();
|
||||||
|
thrdArgs.cond.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadWaitForExit(&writeThread);
|
||||||
|
threadClose(&writeThread);
|
||||||
|
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
|
||||||
|
unzFile unz = unzOpen64(fullPath.c_str());
|
||||||
|
if(unz && fs::zipNotEmpty(unz)) {
|
||||||
|
t->status->setStatus("threadStatusCalculatingSaveSize");
|
||||||
|
uint64_t saveSize = fs::getZipTotalSize(unz);
|
||||||
|
int64_t availSize = 0;
|
||||||
|
fsFsGetTotalSpace(fsdevGetDeviceFileSystem("sv"), "/", &availSize);
|
||||||
|
if ((int)saveSize > availSize) {
|
||||||
|
data::userTitleInfo *utinfo = data::getCurrentUserTitleInfo();
|
||||||
|
fs::unmountSave();
|
||||||
|
fs::extendSaveData(utinfo, saveSize + 0x500000, t);
|
||||||
|
fs::mountSave(utinfo->saveInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::delDir("sv:/");
|
||||||
|
fs::commitToDevice("sv");
|
||||||
|
// fs::copyZipToDir(unz, "sv:/", "sv", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (unz)
|
||||||
|
// unzClose(unz);
|
||||||
|
close(fSocket);
|
||||||
|
|
||||||
|
delete[] buff;
|
||||||
|
fs::delfile(fullPath);
|
||||||
|
|
||||||
|
LDN::waitForDONE(comm->commFD);
|
||||||
|
LDN::sendDONE(comm->commFD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LDN::copyRemoteSaveFile(threadInfo *t) {
|
||||||
|
fs::copyArgs *c = (fs::copyArgs *)t->argPtr;
|
||||||
|
LDN::LDNCommunicate *comm = (LDN::LDNCommunicate *)c->comm;
|
||||||
|
LDN::commMeta meta;
|
||||||
|
|
||||||
|
t->status->setStatus("LDNStatus");
|
||||||
|
reciveMeta(&meta, comm->commFD);
|
||||||
|
if (meta.type == UPDATE_ABORT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (meta.type == UPDATE_FILE) {
|
||||||
|
copyRemoteSaveFileCommit(&meta, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
69
source/threads.cpp
Normal file
69
source/threads.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <switch.h>
|
||||||
|
#include "threads.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static threads::threadProcMngr *threadMngr;
|
||||||
|
|
||||||
|
threads::threadProcMngr::~threadProcMngr()
|
||||||
|
{
|
||||||
|
for(threadInfo *t : threads)
|
||||||
|
{
|
||||||
|
threadWaitForExit(&t->thrd);
|
||||||
|
threadClose(&t->thrd);
|
||||||
|
delete t->status;
|
||||||
|
delete t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
threadInfo *threads::threadProcMngr::newThread(ThreadFunc func, void *args, funcPtr _drawfunc)
|
||||||
|
{
|
||||||
|
threadInfo *t = new threadInfo;
|
||||||
|
t->status = new threadStatus;
|
||||||
|
t->running = false;
|
||||||
|
t->finished = false;
|
||||||
|
t->thrdFunc = func;
|
||||||
|
t->drawFunc = _drawfunc;
|
||||||
|
t->argPtr = args;
|
||||||
|
|
||||||
|
mutexLock(&threadLock);
|
||||||
|
threads.push_back(t);
|
||||||
|
mutexUnlock(&threadLock);
|
||||||
|
return threads[threads.size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void threads::threadProcMngr::update()
|
||||||
|
{
|
||||||
|
if(!threads.empty())
|
||||||
|
{
|
||||||
|
Result res = 0;
|
||||||
|
threadInfo *t = threads[0];
|
||||||
|
if(!t->running && R_SUCCEEDED((res = threadCreate(&t->thrd, t->thrdFunc, t, NULL, 0x80000, 0x2B, 1))))
|
||||||
|
{
|
||||||
|
threadStart(&t->thrd);
|
||||||
|
t->running = true;
|
||||||
|
}
|
||||||
|
else if(!t->running && R_FAILED(res))//Should kill the thread that failed.
|
||||||
|
t->finished = true;
|
||||||
|
else if(t->finished)
|
||||||
|
{
|
||||||
|
threadWaitForExit(&t->thrd);
|
||||||
|
threadClose(&t->thrd);
|
||||||
|
delete t->status;
|
||||||
|
delete t;
|
||||||
|
mutexLock(&threadLock);
|
||||||
|
threads.erase(threads.begin());
|
||||||
|
mutexUnlock(&threadLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
threadInfo *threads::newThread(ThreadFunc func, void *args, funcPtr _drawFunc)
|
||||||
|
{
|
||||||
|
return threadMngr->newThread(func, args, _drawFunc);
|
||||||
|
}
|
||||||
351
source/util.cpp
Normal file
351
source/util.cpp
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
|
||||||
|
#include "fs/file.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
static const uint32_t verboten[] = { L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'};
|
||||||
|
|
||||||
|
static bool isVerboten(const uint32_t& t)
|
||||||
|
{
|
||||||
|
for(unsigned i = 0; i < 13; i++)
|
||||||
|
{
|
||||||
|
if(t == verboten[i])
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::replaceStr(std::string& _str, const std::string& _find, const std::string& _rep)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
while((pos = _str.find(_find)) != _str.npos)
|
||||||
|
_str.replace(pos, _find.length(), _rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Used to split version tag git
|
||||||
|
static void getVersionFromTag(const std::string& tag, unsigned& _year, unsigned& _month, unsigned& _day)
|
||||||
|
{
|
||||||
|
_month = strtoul(tag.substr(0, 2).c_str(), NULL, 10);
|
||||||
|
_day = strtoul(tag.substr(3, 5).c_str(), NULL, 10);
|
||||||
|
_year = strtoul(tag.substr(6, 10).c_str(), NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Missing swkbd config funcs for now
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SwkbdPosStart = 0,
|
||||||
|
SwkbdPosEnd = 1
|
||||||
|
} SwkbdInitPos;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t read[0x32 / sizeof(uint16_t)];
|
||||||
|
uint16_t word[0x32 / sizeof(uint16_t)];
|
||||||
|
} dictWord;
|
||||||
|
|
||||||
|
void swkbdDictWordCreate(dictWord *w, const char *read, const char *word)
|
||||||
|
{
|
||||||
|
memset(w, 0, sizeof(*w));
|
||||||
|
|
||||||
|
utf8_to_utf16(w->read, (uint8_t *)read, (sizeof(w->read) / sizeof(uint16_t)) - 1);
|
||||||
|
utf8_to_utf16(w->word, (uint8_t *)word, (sizeof(w->word) / sizeof(uint16_t)) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t replaceChar(uint32_t c)
|
||||||
|
{
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case L'é':
|
||||||
|
return 'e';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void replaceCharCStr(char *_s, char _find, char _rep)
|
||||||
|
{
|
||||||
|
size_t strLength = strlen(_s);
|
||||||
|
for(unsigned i = 0; i < strLength; i++)
|
||||||
|
{
|
||||||
|
if(_s[i] == _find)
|
||||||
|
_s[i] = _rep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::getDateTime(int fmt)
|
||||||
|
{
|
||||||
|
char ret[128];
|
||||||
|
|
||||||
|
time_t raw;
|
||||||
|
time(&raw);
|
||||||
|
tm *Time = localtime(&raw);
|
||||||
|
|
||||||
|
switch(fmt)
|
||||||
|
{
|
||||||
|
case DATE_FMT_YMD:
|
||||||
|
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATE_FMT_YDM:
|
||||||
|
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mday, Time->tm_mon + 1, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATE_FMT_HOYSTE:
|
||||||
|
sprintf(ret, "%02d.%02d.%04d", Time->tm_mday, Time->tm_mon + 1, Time->tm_year + 1900);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATE_FMT_JHK:
|
||||||
|
sprintf(ret, "%04d%02d%02d_%02d%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATE_FMT_ASC:
|
||||||
|
strcpy(ret, asctime(Time));
|
||||||
|
replaceCharCStr(ret, ':', '_');
|
||||||
|
replaceCharCStr(ret, '\n', 0x00);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
//void util::copyDirListToMenu(const fs::dirList& d, ui::menu& m)
|
||||||
|
//{
|
||||||
|
// m.reset();
|
||||||
|
// m.addOpt(NULL, ".");
|
||||||
|
// m.addOpt(NULL, "..");
|
||||||
|
// for(unsigned i = 0; i < d.getCount(); i++)
|
||||||
|
// {
|
||||||
|
// if(d.isDir(i))
|
||||||
|
// m.addOpt(NULL, "D " + d.getItem(i));
|
||||||
|
// else
|
||||||
|
// m.addOpt(NULL, "F " + d.getItem(i));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
void util::removeLastFolderFromString(std::string& _path)
|
||||||
|
{
|
||||||
|
unsigned last = _path.find_last_of('/', _path.length() - 2);
|
||||||
|
_path.erase(last + 1, _path.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t util::getTotalPlacesInPath(const std::string& _path)
|
||||||
|
{
|
||||||
|
//Skip device
|
||||||
|
size_t pos = _path.find_first_of('/'), ret = 0;
|
||||||
|
while((pos = _path.find_first_of('/', ++pos)) != _path.npos)
|
||||||
|
++ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::trimPath(std::string& _path, uint8_t _places)
|
||||||
|
{
|
||||||
|
size_t pos = _path.find_first_of('/');
|
||||||
|
for(int i = 0; i < _places; i++)
|
||||||
|
pos = _path.find_first_of('/', ++pos);
|
||||||
|
_path = _path.substr(++pos, _path.npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::safeString(const std::string& s)
|
||||||
|
{
|
||||||
|
std::string ret = "";
|
||||||
|
for(unsigned i = 0; i < s.length(); )
|
||||||
|
{
|
||||||
|
uint32_t tmpChr = 0;
|
||||||
|
ssize_t untCnt = decode_utf8(&tmpChr, (uint8_t *)&s.data()[i]);
|
||||||
|
|
||||||
|
i += untCnt;
|
||||||
|
|
||||||
|
tmpChr = replaceChar(tmpChr);
|
||||||
|
|
||||||
|
if(isVerboten(tmpChr))
|
||||||
|
ret += ' ';
|
||||||
|
else if(!isASCII(tmpChr))
|
||||||
|
return ""; //return empty string so titledata::init defaults to titleID
|
||||||
|
else
|
||||||
|
ret += (char)tmpChr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check for spaces at end
|
||||||
|
while(ret[ret.length() - 1] == ' ' || ret[ret.length() - 1] == '.')
|
||||||
|
ret.erase(ret.length() - 1, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string getTimeString(const uint32_t& _h, const uint32_t& _m)
|
||||||
|
{
|
||||||
|
char tmp[32];
|
||||||
|
sprintf(tmp, "%02d:%02d", _h, _m);
|
||||||
|
return std::string(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[])
|
||||||
|
{
|
||||||
|
SwkbdConfig swkbd;
|
||||||
|
swkbdCreate(&swkbd, dictCnt);
|
||||||
|
swkbdConfigSetBlurBackground(&swkbd, true);
|
||||||
|
swkbdConfigSetInitialText(&swkbd, def.c_str());
|
||||||
|
swkbdConfigSetHeaderText(&swkbd, head.c_str());
|
||||||
|
swkbdConfigSetGuideText(&swkbd, head.c_str());
|
||||||
|
swkbdConfigSetInitialCursorPos(&swkbd, SwkbdPosEnd);
|
||||||
|
swkbdConfigSetType(&swkbd, _type);
|
||||||
|
swkbdConfigSetStringLenMax(&swkbd, maxLength);
|
||||||
|
swkbdConfigSetKeySetDisableBitmask(&swkbd, SwkbdKeyDisableBitmask_Backslash | SwkbdKeyDisableBitmask_Percent);
|
||||||
|
swkbdConfigSetDicFlag(&swkbd, 1);
|
||||||
|
|
||||||
|
if(dictCnt > 0)
|
||||||
|
{
|
||||||
|
dictWord words[dictCnt];
|
||||||
|
for(unsigned i = 0; i < dictCnt; i++)
|
||||||
|
swkbdDictWordCreate(&words[i], dictWords[i].c_str(), dictWords[i].c_str());
|
||||||
|
|
||||||
|
swkbdConfigSetDictionary(&swkbd, (SwkbdDictWord *)words, dictCnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
char out[maxLength + 1];
|
||||||
|
memset(out, 0, maxLength + 1);
|
||||||
|
swkbdShow(&swkbd, out, maxLength + 1);
|
||||||
|
swkbdClose(&swkbd);
|
||||||
|
|
||||||
|
return std::string(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::getExtensionFromString(const std::string& get)
|
||||||
|
{
|
||||||
|
size_t ext = get.find_last_of('.');
|
||||||
|
if(ext != get.npos)
|
||||||
|
return get.substr(ext + 1, get.npos);
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::getFilenameFromPath(const std::string& get)
|
||||||
|
{
|
||||||
|
size_t nameStart = get.find_last_of('/');
|
||||||
|
if(nameStart != get.npos)
|
||||||
|
return get.substr(nameStart + 1, get.npos);
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::generateAbbrev(const uint64_t& tid)
|
||||||
|
{
|
||||||
|
data::titleInfo *tmp = data::getTitleInfoByTID(tid);
|
||||||
|
size_t titleLength = tmp->safeTitle.length();
|
||||||
|
|
||||||
|
char temp[titleLength + 1];
|
||||||
|
memset(temp, 0, titleLength + 1);
|
||||||
|
memcpy(temp, tmp->safeTitle.c_str(), titleLength);
|
||||||
|
|
||||||
|
std::string ret;
|
||||||
|
char *tok = strtok(temp, " ");
|
||||||
|
while(tok)
|
||||||
|
{
|
||||||
|
if(isASCII(tok[0]))
|
||||||
|
ret += tok[0];
|
||||||
|
tok = strtok(NULL, " ");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::stripChar(char _c, std::string& _s)
|
||||||
|
{
|
||||||
|
size_t pos = 0;
|
||||||
|
while((pos = _s.find(_c)) != _s.npos)
|
||||||
|
_s.erase(pos, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::replaceButtonsInString(std::string& rep)
|
||||||
|
{
|
||||||
|
replaceStr(rep, "[A]", "\ue0e0");
|
||||||
|
replaceStr(rep, "[B]", "\ue0e1");
|
||||||
|
replaceStr(rep, "[X]", "\ue0e2");
|
||||||
|
replaceStr(rep, "[Y]", "\ue0e3");
|
||||||
|
replaceStr(rep, "[L]", "\ue0e4");
|
||||||
|
replaceStr(rep, "[R]", "\ue0e5");
|
||||||
|
replaceStr(rep, "[ZL]", "\ue0e6");
|
||||||
|
replaceStr(rep, "[ZR]", "\ue0e7");
|
||||||
|
replaceStr(rep, "[SL]", "\ue0e8");
|
||||||
|
replaceStr(rep, "[SR]", "\ue0e9");
|
||||||
|
replaceStr(rep, "[DPAD]", "\ue0ea");
|
||||||
|
replaceStr(rep, "[DUP]", "\ue0eb");
|
||||||
|
replaceStr(rep, "[DDOWN]", "\ue0ec");
|
||||||
|
replaceStr(rep, "[DLEFT]", "\ue0ed");
|
||||||
|
replaceStr(rep, "[DRIGHT]", "\ue0ee");
|
||||||
|
replaceStr(rep, "[+]", "\ue0ef");
|
||||||
|
replaceStr(rep, "[-]", "\ue0f0");
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::sysBoost()
|
||||||
|
{
|
||||||
|
if(R_FAILED(clkrstInitialize()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ClkrstSession cpu, gpu, ram;
|
||||||
|
clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3);
|
||||||
|
clkrstOpenSession(&gpu, PcvModuleId_GPU, 3);
|
||||||
|
clkrstOpenSession(&ram, PcvModuleId_EMC, 3);
|
||||||
|
|
||||||
|
clkrstSetClockRate(&cpu, util::CPU_SPEED_1785MHz);
|
||||||
|
clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz);
|
||||||
|
clkrstSetClockRate(&ram, util::RAM_SPEED_1600MHz);
|
||||||
|
|
||||||
|
clkrstCloseSession(&cpu);
|
||||||
|
clkrstCloseSession(&gpu);
|
||||||
|
clkrstCloseSession(&ram);
|
||||||
|
clkrstExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void util::sysNormal()
|
||||||
|
{
|
||||||
|
if(R_FAILED(clkrstInitialize()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ClkrstSession cpu, gpu, ram;
|
||||||
|
clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3);
|
||||||
|
clkrstOpenSession(&gpu, PcvModuleId_GPU, 3);
|
||||||
|
clkrstOpenSession(&ram, PcvModuleId_EMC, 3);
|
||||||
|
|
||||||
|
clkrstSetClockRate(&cpu, util::CPU_SPEED_1020MHz);
|
||||||
|
clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz);
|
||||||
|
clkrstSetClockRate(&ram, util::RAM_SPEED_1331MHz);
|
||||||
|
|
||||||
|
clkrstCloseSession(&cpu);
|
||||||
|
clkrstCloseSession(&gpu);
|
||||||
|
clkrstCloseSession(&ram);
|
||||||
|
clkrstExit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::getSizeString(const uint64_t& _size)
|
||||||
|
{
|
||||||
|
char sizeStr[32];
|
||||||
|
if(_size >= 0x40000000)
|
||||||
|
sprintf(sizeStr, "%.2fGB", (float)_size / 1024.0f / 1024.0f / 1024.0f);
|
||||||
|
else if(_size >= 0x100000)
|
||||||
|
sprintf(sizeStr, "%.2fMB", (float)_size / 1024.0f / 1024.0f);
|
||||||
|
else if(_size >= 0x400)
|
||||||
|
sprintf(sizeStr, "%.2fKB", (float)_size / 1024.0f);
|
||||||
|
else
|
||||||
|
sprintf(sizeStr, "%lu Bytes", _size);
|
||||||
|
return std::string(sizeStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result util::accountDeleteUser(AccountUid *uid)
|
||||||
|
{
|
||||||
|
Service *account = accountGetServiceSession();
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
AccountUid uid;
|
||||||
|
} in = {*uid};
|
||||||
|
|
||||||
|
return serviceDispatchIn(account, 203, in);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user