initial commit
This commit is contained in:
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.
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.
@@ -0,0 +1,3 @@
|
||||
[submodule "lib"]
|
||||
path = lib
|
||||
url = https://github.com/XorTroll/Plutonium.git
|
||||
Generated
+8
@@ -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
|
||||
Generated
+18
@@ -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>
|
||||
Generated
+7
@@ -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>
|
||||
Vendored
+23
@@ -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
|
||||
}
|
||||
Vendored
+101
@@ -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"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -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
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
Vendored
BIN
Binary file not shown.
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#define BACKGROUND_COLOR COLOR("00FFFFFF")
|
||||
#define COLOR(hex) pu::ui::Color::FromHex(hex)
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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, ...);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
Submodule
+1
Submodule lib added at b56564b70d
Vendored
BIN
Binary file not shown.
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,351 @@
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <sys/stat.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "fs/file.h"
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "type.h"
|
||||
|
||||
static const uint32_t verboten[] = { L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'};
|
||||
|
||||
static bool isVerboten(const uint32_t& t)
|
||||
{
|
||||
for(unsigned i = 0; i < 13; i++)
|
||||
{
|
||||
if(t == verboten[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void util::replaceStr(std::string& _str, const std::string& _find, const std::string& _rep)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = _str.find(_find)) != _str.npos)
|
||||
_str.replace(pos, _find.length(), _rep);
|
||||
}
|
||||
|
||||
//Used to split version tag git
|
||||
static void getVersionFromTag(const std::string& tag, unsigned& _year, unsigned& _month, unsigned& _day)
|
||||
{
|
||||
_month = strtoul(tag.substr(0, 2).c_str(), NULL, 10);
|
||||
_day = strtoul(tag.substr(3, 5).c_str(), NULL, 10);
|
||||
_year = strtoul(tag.substr(6, 10).c_str(), NULL, 10);
|
||||
}
|
||||
|
||||
//Missing swkbd config funcs for now
|
||||
typedef enum
|
||||
{
|
||||
SwkbdPosStart = 0,
|
||||
SwkbdPosEnd = 1
|
||||
} SwkbdInitPos;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t read[0x32 / sizeof(uint16_t)];
|
||||
uint16_t word[0x32 / sizeof(uint16_t)];
|
||||
} dictWord;
|
||||
|
||||
void swkbdDictWordCreate(dictWord *w, const char *read, const char *word)
|
||||
{
|
||||
memset(w, 0, sizeof(*w));
|
||||
|
||||
utf8_to_utf16(w->read, (uint8_t *)read, (sizeof(w->read) / sizeof(uint16_t)) - 1);
|
||||
utf8_to_utf16(w->word, (uint8_t *)word, (sizeof(w->word) / sizeof(uint16_t)) - 1);
|
||||
}
|
||||
|
||||
uint32_t replaceChar(uint32_t c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case L'é':
|
||||
return 'e';
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void replaceCharCStr(char *_s, char _find, char _rep)
|
||||
{
|
||||
size_t strLength = strlen(_s);
|
||||
for(unsigned i = 0; i < strLength; i++)
|
||||
{
|
||||
if(_s[i] == _find)
|
||||
_s[i] = _rep;
|
||||
}
|
||||
}
|
||||
|
||||
std::string util::getDateTime(int fmt)
|
||||
{
|
||||
char ret[128];
|
||||
|
||||
time_t raw;
|
||||
time(&raw);
|
||||
tm *Time = localtime(&raw);
|
||||
|
||||
switch(fmt)
|
||||
{
|
||||
case DATE_FMT_YMD:
|
||||
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||
break;
|
||||
|
||||
case DATE_FMT_YDM:
|
||||
sprintf(ret, "%04d.%02d.%02d @ %02d.%02d.%02d", Time->tm_year + 1900, Time->tm_mday, Time->tm_mon + 1, Time->tm_hour, Time->tm_min, Time->tm_sec);
|
||||
break;
|
||||
|
||||
case DATE_FMT_HOYSTE:
|
||||
sprintf(ret, "%02d.%02d.%04d", Time->tm_mday, Time->tm_mon + 1, Time->tm_year + 1900);
|
||||
break;
|
||||
|
||||
case DATE_FMT_JHK:
|
||||
sprintf(ret, "%04d%02d%02d_%02d%02d", Time->tm_year + 1900, Time->tm_mon + 1, Time->tm_mday, Time->tm_hour, Time->tm_min);
|
||||
break;
|
||||
|
||||
case DATE_FMT_ASC:
|
||||
strcpy(ret, asctime(Time));
|
||||
replaceCharCStr(ret, ':', '_');
|
||||
replaceCharCStr(ret, '\n', 0x00);
|
||||
break;
|
||||
}
|
||||
|
||||
return std::string(ret);
|
||||
}
|
||||
|
||||
//void util::copyDirListToMenu(const fs::dirList& d, ui::menu& m)
|
||||
//{
|
||||
// m.reset();
|
||||
// m.addOpt(NULL, ".");
|
||||
// m.addOpt(NULL, "..");
|
||||
// for(unsigned i = 0; i < d.getCount(); i++)
|
||||
// {
|
||||
// if(d.isDir(i))
|
||||
// m.addOpt(NULL, "D " + d.getItem(i));
|
||||
// else
|
||||
// m.addOpt(NULL, "F " + d.getItem(i));
|
||||
// }
|
||||
//}
|
||||
|
||||
void util::removeLastFolderFromString(std::string& _path)
|
||||
{
|
||||
unsigned last = _path.find_last_of('/', _path.length() - 2);
|
||||
_path.erase(last + 1, _path.length());
|
||||
}
|
||||
|
||||
size_t util::getTotalPlacesInPath(const std::string& _path)
|
||||
{
|
||||
//Skip device
|
||||
size_t pos = _path.find_first_of('/'), ret = 0;
|
||||
while((pos = _path.find_first_of('/', ++pos)) != _path.npos)
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void util::trimPath(std::string& _path, uint8_t _places)
|
||||
{
|
||||
size_t pos = _path.find_first_of('/');
|
||||
for(int i = 0; i < _places; i++)
|
||||
pos = _path.find_first_of('/', ++pos);
|
||||
_path = _path.substr(++pos, _path.npos);
|
||||
}
|
||||
|
||||
std::string util::safeString(const std::string& s)
|
||||
{
|
||||
std::string ret = "";
|
||||
for(unsigned i = 0; i < s.length(); )
|
||||
{
|
||||
uint32_t tmpChr = 0;
|
||||
ssize_t untCnt = decode_utf8(&tmpChr, (uint8_t *)&s.data()[i]);
|
||||
|
||||
i += untCnt;
|
||||
|
||||
tmpChr = replaceChar(tmpChr);
|
||||
|
||||
if(isVerboten(tmpChr))
|
||||
ret += ' ';
|
||||
else if(!isASCII(tmpChr))
|
||||
return ""; //return empty string so titledata::init defaults to titleID
|
||||
else
|
||||
ret += (char)tmpChr;
|
||||
}
|
||||
|
||||
//Check for spaces at end
|
||||
while(ret[ret.length() - 1] == ' ' || ret[ret.length() - 1] == '.')
|
||||
ret.erase(ret.length() - 1, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline std::string getTimeString(const uint32_t& _h, const uint32_t& _m)
|
||||
{
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%02d:%02d", _h, _m);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
std::string util::getStringInput(SwkbdType _type, const std::string& def, const std::string& head, size_t maxLength, unsigned dictCnt, const std::string dictWords[])
|
||||
{
|
||||
SwkbdConfig swkbd;
|
||||
swkbdCreate(&swkbd, dictCnt);
|
||||
swkbdConfigSetBlurBackground(&swkbd, true);
|
||||
swkbdConfigSetInitialText(&swkbd, def.c_str());
|
||||
swkbdConfigSetHeaderText(&swkbd, head.c_str());
|
||||
swkbdConfigSetGuideText(&swkbd, head.c_str());
|
||||
swkbdConfigSetInitialCursorPos(&swkbd, SwkbdPosEnd);
|
||||
swkbdConfigSetType(&swkbd, _type);
|
||||
swkbdConfigSetStringLenMax(&swkbd, maxLength);
|
||||
swkbdConfigSetKeySetDisableBitmask(&swkbd, SwkbdKeyDisableBitmask_Backslash | SwkbdKeyDisableBitmask_Percent);
|
||||
swkbdConfigSetDicFlag(&swkbd, 1);
|
||||
|
||||
if(dictCnt > 0)
|
||||
{
|
||||
dictWord words[dictCnt];
|
||||
for(unsigned i = 0; i < dictCnt; i++)
|
||||
swkbdDictWordCreate(&words[i], dictWords[i].c_str(), dictWords[i].c_str());
|
||||
|
||||
swkbdConfigSetDictionary(&swkbd, (SwkbdDictWord *)words, dictCnt);
|
||||
}
|
||||
|
||||
char out[maxLength + 1];
|
||||
memset(out, 0, maxLength + 1);
|
||||
swkbdShow(&swkbd, out, maxLength + 1);
|
||||
swkbdClose(&swkbd);
|
||||
|
||||
return std::string(out);
|
||||
}
|
||||
|
||||
std::string util::getExtensionFromString(const std::string& get)
|
||||
{
|
||||
size_t ext = get.find_last_of('.');
|
||||
if(ext != get.npos)
|
||||
return get.substr(ext + 1, get.npos);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string util::getFilenameFromPath(const std::string& get)
|
||||
{
|
||||
size_t nameStart = get.find_last_of('/');
|
||||
if(nameStart != get.npos)
|
||||
return get.substr(nameStart + 1, get.npos);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string util::generateAbbrev(const uint64_t& tid)
|
||||
{
|
||||
data::titleInfo *tmp = data::getTitleInfoByTID(tid);
|
||||
size_t titleLength = tmp->safeTitle.length();
|
||||
|
||||
char temp[titleLength + 1];
|
||||
memset(temp, 0, titleLength + 1);
|
||||
memcpy(temp, tmp->safeTitle.c_str(), titleLength);
|
||||
|
||||
std::string ret;
|
||||
char *tok = strtok(temp, " ");
|
||||
while(tok)
|
||||
{
|
||||
if(isASCII(tok[0]))
|
||||
ret += tok[0];
|
||||
tok = strtok(NULL, " ");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void util::stripChar(char _c, std::string& _s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = _s.find(_c)) != _s.npos)
|
||||
_s.erase(pos, 1);
|
||||
}
|
||||
|
||||
void util::replaceButtonsInString(std::string& rep)
|
||||
{
|
||||
replaceStr(rep, "[A]", "\ue0e0");
|
||||
replaceStr(rep, "[B]", "\ue0e1");
|
||||
replaceStr(rep, "[X]", "\ue0e2");
|
||||
replaceStr(rep, "[Y]", "\ue0e3");
|
||||
replaceStr(rep, "[L]", "\ue0e4");
|
||||
replaceStr(rep, "[R]", "\ue0e5");
|
||||
replaceStr(rep, "[ZL]", "\ue0e6");
|
||||
replaceStr(rep, "[ZR]", "\ue0e7");
|
||||
replaceStr(rep, "[SL]", "\ue0e8");
|
||||
replaceStr(rep, "[SR]", "\ue0e9");
|
||||
replaceStr(rep, "[DPAD]", "\ue0ea");
|
||||
replaceStr(rep, "[DUP]", "\ue0eb");
|
||||
replaceStr(rep, "[DDOWN]", "\ue0ec");
|
||||
replaceStr(rep, "[DLEFT]", "\ue0ed");
|
||||
replaceStr(rep, "[DRIGHT]", "\ue0ee");
|
||||
replaceStr(rep, "[+]", "\ue0ef");
|
||||
replaceStr(rep, "[-]", "\ue0f0");
|
||||
}
|
||||
|
||||
void util::sysBoost()
|
||||
{
|
||||
if(R_FAILED(clkrstInitialize()))
|
||||
return;
|
||||
|
||||
ClkrstSession cpu, gpu, ram;
|
||||
clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3);
|
||||
clkrstOpenSession(&gpu, PcvModuleId_GPU, 3);
|
||||
clkrstOpenSession(&ram, PcvModuleId_EMC, 3);
|
||||
|
||||
clkrstSetClockRate(&cpu, util::CPU_SPEED_1785MHz);
|
||||
clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz);
|
||||
clkrstSetClockRate(&ram, util::RAM_SPEED_1600MHz);
|
||||
|
||||
clkrstCloseSession(&cpu);
|
||||
clkrstCloseSession(&gpu);
|
||||
clkrstCloseSession(&ram);
|
||||
clkrstExit();
|
||||
}
|
||||
|
||||
void util::sysNormal()
|
||||
{
|
||||
if(R_FAILED(clkrstInitialize()))
|
||||
return;
|
||||
|
||||
ClkrstSession cpu, gpu, ram;
|
||||
clkrstOpenSession(&cpu, PcvModuleId_CpuBus, 3);
|
||||
clkrstOpenSession(&gpu, PcvModuleId_GPU, 3);
|
||||
clkrstOpenSession(&ram, PcvModuleId_EMC, 3);
|
||||
|
||||
clkrstSetClockRate(&cpu, util::CPU_SPEED_1020MHz);
|
||||
clkrstSetClockRate(&gpu, util::GPU_SPEED_76MHz);
|
||||
clkrstSetClockRate(&ram, util::RAM_SPEED_1331MHz);
|
||||
|
||||
clkrstCloseSession(&cpu);
|
||||
clkrstCloseSession(&gpu);
|
||||
clkrstCloseSession(&ram);
|
||||
clkrstExit();
|
||||
|
||||
}
|
||||
|
||||
std::string util::getSizeString(const uint64_t& _size)
|
||||
{
|
||||
char sizeStr[32];
|
||||
if(_size >= 0x40000000)
|
||||
sprintf(sizeStr, "%.2fGB", (float)_size / 1024.0f / 1024.0f / 1024.0f);
|
||||
else if(_size >= 0x100000)
|
||||
sprintf(sizeStr, "%.2fMB", (float)_size / 1024.0f / 1024.0f);
|
||||
else if(_size >= 0x400)
|
||||
sprintf(sizeStr, "%.2fKB", (float)_size / 1024.0f);
|
||||
else
|
||||
sprintf(sizeStr, "%lu Bytes", _size);
|
||||
return std::string(sizeStr);
|
||||
}
|
||||
|
||||
Result util::accountDeleteUser(AccountUid *uid)
|
||||
{
|
||||
Service *account = accountGetServiceSession();
|
||||
struct
|
||||
{
|
||||
AccountUid uid;
|
||||
} in = {*uid};
|
||||
|
||||
return serviceDispatchIn(account, 203, in);
|
||||
}
|
||||
Reference in New Issue
Block a user