initial commit

This commit is contained in:
2024-07-14 20:20:03 +03:00
commit 31953464e7
95 changed files with 4688 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "lib"]
path = lib
url = https://github.com/XorTroll/Plutonium.git

8
.idea/.gitignore generated vendored Normal file
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

203
Makefile Normal file
View 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
#---------------------------------------------------------------------------------------

2
NXST.lst Normal file
View File

@@ -0,0 +1,2 @@
-CSn
/Users/n.fedorov/dev/NXST/NXST.elf

10
cleanup Executable file
View 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
View 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
icon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
include/.DS_Store vendored Normal file

Binary file not shown.

View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
#define BACKGROUND_COLOR COLOR("00FFFFFF")
#define COLOR(hex) pu::ui::Color::FromHex(hex)

91
include/data.h Normal file
View 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
View 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
View 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
View 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
View File

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

46
include/fs/fstype.h Normal file
View 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
View 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
View File

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

44
include/threads.h Normal file
View 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
View 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
View 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

Submodule lib added at b56564b70d

BIN
source/.DS_Store vendored Normal file

Binary file not shown.

59
source/Main.cpp Normal file
View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

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

251
source/fs/zip.cpp Normal file
View 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
View 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
View 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
View 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);
}