diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index b8a22bf..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx b/.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx deleted file mode 100644 index 0da004a..0000000 Binary files a/.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx and /dev/null differ diff --git a/.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx b/.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx deleted file mode 100644 index 3c7e1ab..0000000 Binary files a/.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx and /dev/null differ diff --git a/.cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx b/.cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx deleted file mode 100644 index a71b2ca..0000000 Binary files a/.cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx and /dev/null differ diff --git a/.cache/clangd/index/Plutonium.176CA4CACA9A1290.idx b/.cache/clangd/index/Plutonium.176CA4CACA9A1290.idx deleted file mode 100644 index 1e8ad35..0000000 Binary files a/.cache/clangd/index/Plutonium.176CA4CACA9A1290.idx and /dev/null differ diff --git a/.cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx b/.cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx deleted file mode 100644 index 296e136..0000000 Binary files a/.cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx and /dev/null differ diff --git a/.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx b/.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx deleted file mode 100644 index 0416f1d..0000000 Binary files a/.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx and /dev/null differ diff --git a/.cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx b/.cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx deleted file mode 100644 index 14527b7..0000000 Binary files a/.cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx and /dev/null differ diff --git a/.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx b/.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx deleted file mode 100644 index 66a84d7..0000000 Binary files a/.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx and /dev/null differ diff --git a/.cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx b/.cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx deleted file mode 100644 index e1bc31f..0000000 Binary files a/.cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx and /dev/null differ diff --git a/.cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx b/.cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx deleted file mode 100644 index 229f104..0000000 Binary files a/.cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx and /dev/null differ diff --git a/.cache/clangd/index/const.h.BA34DD522C225A5C.idx b/.cache/clangd/index/const.h.BA34DD522C225A5C.idx deleted file mode 100644 index 436dc3f..0000000 Binary files a/.cache/clangd/index/const.h.BA34DD522C225A5C.idx and /dev/null differ diff --git a/.cache/clangd/index/data.cpp.92E1D4C612A67155.idx b/.cache/clangd/index/data.cpp.92E1D4C612A67155.idx deleted file mode 100644 index f4b8421..0000000 Binary files a/.cache/clangd/index/data.cpp.92E1D4C612A67155.idx and /dev/null differ diff --git a/.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx b/.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx deleted file mode 100644 index f72c8d3..0000000 Binary files a/.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx and /dev/null differ diff --git a/.cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx b/.cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx deleted file mode 100644 index cbd16e0..0000000 Binary files a/.cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx and /dev/null differ diff --git a/.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx b/.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx deleted file mode 100644 index 7daac4a..0000000 Binary files a/.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx b/.cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx deleted file mode 100644 index 63bb953..0000000 Binary files a/.cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx b/.cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx deleted file mode 100644 index fd2b076..0000000 Binary files a/.cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx b/.cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx deleted file mode 100644 index 625d0ef..0000000 Binary files a/.cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx b/.cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx deleted file mode 100644 index f958d1d..0000000 Binary files a/.cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx b/.cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx deleted file mode 100644 index ccf5285..0000000 Binary files a/.cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx b/.cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx deleted file mode 100644 index b0fd7c1..0000000 Binary files a/.cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx b/.cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx deleted file mode 100644 index c35e700..0000000 Binary files a/.cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx and /dev/null differ diff --git a/.cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx b/.cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx deleted file mode 100644 index e588c34..0000000 Binary files a/.cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx and /dev/null differ diff --git a/.cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx b/.cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx deleted file mode 100644 index abb66ef..0000000 Binary files a/.cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx and /dev/null differ diff --git a/.cache/clangd/index/file.cpp.5237F76D2447B003.idx b/.cache/clangd/index/file.cpp.5237F76D2447B003.idx deleted file mode 100644 index a0e56d5..0000000 Binary files a/.cache/clangd/index/file.cpp.5237F76D2447B003.idx and /dev/null differ diff --git a/.cache/clangd/index/file.h.ECED60B58854FB92.idx b/.cache/clangd/index/file.h.ECED60B58854FB92.idx deleted file mode 100644 index 18e999a..0000000 Binary files a/.cache/clangd/index/file.h.ECED60B58854FB92.idx and /dev/null differ diff --git a/.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx b/.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx deleted file mode 100644 index fb70b75..0000000 Binary files a/.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx and /dev/null differ diff --git a/.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx b/.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx deleted file mode 100644 index d74e017..0000000 Binary files a/.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx and /dev/null differ diff --git a/.cache/clangd/index/fsfile.c.867FA10C150715C4.idx b/.cache/clangd/index/fsfile.c.867FA10C150715C4.idx deleted file mode 100644 index 4cf7d01..0000000 Binary files a/.cache/clangd/index/fsfile.c.867FA10C150715C4.idx and /dev/null differ diff --git a/.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx b/.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx deleted file mode 100644 index 0a4a006..0000000 Binary files a/.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx and /dev/null differ diff --git a/.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx b/.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx deleted file mode 100644 index 0999c49..0000000 Binary files a/.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx and /dev/null differ diff --git a/.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx b/.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx deleted file mode 100644 index b21627a..0000000 Binary files a/.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx and /dev/null differ diff --git a/.cache/clangd/index/ldn.h.1D557AF101A575C7.idx b/.cache/clangd/index/ldn.h.1D557AF101A575C7.idx deleted file mode 100644 index 8479681..0000000 Binary files a/.cache/clangd/index/ldn.h.1D557AF101A575C7.idx and /dev/null differ diff --git a/.cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx b/.cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx deleted file mode 100644 index b0c8ec7..0000000 Binary files a/.cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx and /dev/null differ diff --git a/.cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx b/.cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx deleted file mode 100644 index 3151c93..0000000 Binary files a/.cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx and /dev/null differ diff --git a/.cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx b/.cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx deleted file mode 100644 index e5ba05e..0000000 Binary files a/.cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx and /dev/null differ diff --git a/.cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx b/.cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx deleted file mode 100644 index 8b1bb26..0000000 Binary files a/.cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx and /dev/null differ diff --git a/.cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx b/.cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx deleted file mode 100644 index 54941a3..0000000 Binary files a/.cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx and /dev/null differ diff --git a/.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx b/.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx deleted file mode 100644 index 195e416..0000000 Binary files a/.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx and /dev/null differ diff --git a/.cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx b/.cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx deleted file mode 100644 index 5c3e736..0000000 Binary files a/.cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx and /dev/null differ diff --git a/.cache/clangd/index/type.h.3039A978BD63D81E.idx b/.cache/clangd/index/type.h.3039A978BD63D81E.idx deleted file mode 100644 index fbf3bf8..0000000 Binary files a/.cache/clangd/index/type.h.3039A978BD63D81E.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx b/.cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx deleted file mode 100644 index 56db13c..0000000 Binary files a/.cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx b/.cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx deleted file mode 100644 index c44e358..0000000 Binary files a/.cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx b/.cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx deleted file mode 100644 index a333316..0000000 Binary files a/.cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx b/.cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx deleted file mode 100644 index 4ba4ad4..0000000 Binary files a/.cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx b/.cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx deleted file mode 100644 index 44f18c4..0000000 Binary files a/.cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx and /dev/null differ diff --git a/.cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx b/.cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx deleted file mode 100644 index 88e3fa2..0000000 Binary files a/.cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx and /dev/null differ diff --git a/.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx b/.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx deleted file mode 100644 index 0b7254c..0000000 Binary files a/.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx and /dev/null differ diff --git a/.cache/clangd/index/util.h.4B818D27750FB9A3.idx b/.cache/clangd/index/util.h.4B818D27750FB9A3.idx deleted file mode 100644 index 74af237..0000000 Binary files a/.cache/clangd/index/util.h.4B818D27750FB9A3.idx and /dev/null differ diff --git a/.cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx b/.cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx deleted file mode 100644 index 62ef634..0000000 Binary files a/.cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx and /dev/null differ diff --git a/.cache/clangd/index/zip.h.74C41B03268B991E.idx b/.cache/clangd/index/zip.h.74C41B03268B991E.idx deleted file mode 100644 index 2f617a6..0000000 Binary files a/.cache/clangd/index/zip.h.74C41B03268B991E.idx and /dev/null differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d54a32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +.DS_Store + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +server +client diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 53624c9..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index e6bbd32..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d59c8e2..65a3fb4 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,23 +1,30 @@ { - "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 -} + "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/**", + "/usr/local/include/**", + "/opt/homebrew/opt/lz4/include/**", + "/opt/devkitpro/devkitA64/aarch64-none-elf/include/machine" + ], + "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 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 67b7939..0df8e3f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,30 +3,23 @@ "string.h": "c", "stdlib.h": "c", "switch.h": "c", - "psm.h": "c", - "vector": "cpp", - "any": "cpp", + "optional": "cpp", "array": "cpp", + "mutex": "cpp", + "any": "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", @@ -34,7 +27,6 @@ "cstdlib": "cpp", "cstring": "cpp", "ctime": "cpp", - "cuchar": "cpp", "cwchar": "cpp", "cwctype": "cpp", "deque": "cpp", @@ -44,20 +36,16 @@ "set": "cpp", "string": "cpp", "unordered_map": "cpp", - "unordered_set": "cpp", + "vector": "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", @@ -65,37 +53,48 @@ "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", + "cinttypes": "cpp", "typeinfo": "cpp", "valarray": "cpp", "variant": "cpp", "filesystem": "cpp", - "plutonium": "cpp" + "plutonium": "cpp", + "barrier": "cpp", + "cfenv": "cpp", + "complex": "cpp", + "coroutine": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cuchar": "cpp", + "unordered_set": "cpp", + "expected": "cpp", + "regex": "cpp", + "source_location": "cpp", + "future": "cpp", + "latch": "cpp", + "scoped_allocator": "cpp", + "shared_mutex": "cpp", + "spanstream": "cpp", + "stacktrace": "cpp", + "stdfloat": "cpp", + "syncstream": "cpp", + "typeindex": "cpp" } } \ No newline at end of file diff --git a/Makefile b/Makefile index c894e4d..b2a96f1 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS += -g -O2 -ffunction-sections \ $(ARCH) $(DEFINES) -CFLAGS += $(INCLUDE) -D__SWITCH__ +CFLAGS += $(INCLUDE) -D__SWITCH__ -D_GNU_SOURCE=1 CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 @@ -153,8 +153,7 @@ $(BUILD): #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf - + @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf $(TARGET).lst #--------------------------------------------------------------------------------- send: $(BUILD) diff --git a/NXST.lst b/NXST.lst deleted file mode 100644 index 3ad15de..0000000 --- a/NXST.lst +++ /dev/null @@ -1,2 +0,0 @@ --CSn -/Users/n.fedorov/dev/NXST/NXST.elf diff --git a/cleanup b/cleanup deleted file mode 100755 index 344a357..0000000 --- a/cleanup +++ /dev/null @@ -1,10 +0,0 @@ -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 - - diff --git a/compile_commands.json b/compile_commands.json deleted file mode 100644 index 3f01e6a..0000000 --- a/compile_commands.json +++ /dev/null @@ -1,428 +0,0 @@ -[ - { - "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" - } -] diff --git a/deps/asprintf/asprintf.c b/deps/asprintf/asprintf.c new file mode 100644 index 0000000..1d25035 --- /dev/null +++ b/deps/asprintf/asprintf.c @@ -0,0 +1,37 @@ +#include +#include + +#include "asprintf.h" + +int vasprintf(char **strp, const char *fmt, va_list ap) { + int size, res; + + va_list cp; + va_copy(cp, ap); + size = vsnprintf(NULL, 0, fmt, cp); + va_end(cp); + if (size < 0) + return -1; + + *strp = (char *)malloc(size + 1); + if (*strp == NULL) + return -1; + + res = vsnprintf(*strp, size + 1, fmt, ap); + if (res < 0) { + free(*strp); + return -1; + } + + return res; +} + +int asprintf(char **s, const char *fmt, ...) { + int ret; + + va_list ap; + va_start(ap, fmt); + ret = vasprintf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/deps/asprintf/asprintf.h b/deps/asprintf/asprintf.h new file mode 100644 index 0000000..3b40a04 --- /dev/null +++ b/deps/asprintf/asprintf.h @@ -0,0 +1,10 @@ +#ifndef HAVE_ASPRINTF +#define HAVE_ASPRINTF 1 + +#include + +int vasprintf(char **strp, const char *fmt, va_list ap); + +int asprintf(char **s, const char *fmt, ...); + +#endif diff --git a/deps/asprintf/clib.json b/deps/asprintf/clib.json new file mode 100644 index 0000000..4e2b86d --- /dev/null +++ b/deps/asprintf/clib.json @@ -0,0 +1,9 @@ +{ + "name": "asprintf", + "version": "1.0.0", + "repo": "Neved4/asprintf", + "description": "asprintf, vasprintf - print to allocated string", + "license": "MIT", + "keywords": ["asprintf", "sprintf", "alloc", "string"], + "src": ["asprintf.c", "asprintf.h"] +} diff --git a/include/.DS_Store b/include/.DS_Store deleted file mode 100644 index 5008ddf..0000000 Binary files a/include/.DS_Store and /dev/null differ diff --git a/include/TitlesLayout.hpp b/include/TitlesLayout.hpp index 747a1a2..038b53b 100644 --- a/include/TitlesLayout.hpp +++ b/include/TitlesLayout.hpp @@ -1,6 +1,6 @@ #include #include -#include +#include namespace ui { class TitlesLayout : public pu::ui::Layout { diff --git a/include/account.hpp b/include/account.hpp new file mode 100644 index 0000000..b22c07b --- /dev/null +++ b/include/account.hpp @@ -0,0 +1,74 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef ACCOUNT_HPP +#define ACCOUNT_HPP + +#include +#include +#include +#include +#include + +#define USER_ICON_SIZE 64 + +namespace std { + template <> + struct hash { + size_t operator()(const AccountUid& a) const { return ((hash()(a.uid[0]) ^ (hash()(a.uid[1]) << 1)) >> 1); } + }; +} + +inline bool operator==(const AccountUid& x, const AccountUid& y) +{ + return x.uid[0] == y.uid[0] && x.uid[1] == y.uid[1]; +} + +inline bool operator==(const AccountUid& x, u64 y) +{ + return x.uid[0] == y && x.uid[1] == y; +} + +inline bool operator<(const AccountUid& x, const AccountUid& y) +{ + return x.uid[0] < y.uid[0] && x.uid[1] == y.uid[1]; +} + +struct User { + AccountUid id; + std::string name; +}; + +namespace Account { + Result init(void); + void exit(void); + + std::vector ids(void); + AccountUid selectAccount(void); + std::string username(AccountUid id); +} + +#endif \ No newline at end of file diff --git a/include/client.hpp b/include/client.hpp new file mode 100644 index 0000000..8cbbbb9 --- /dev/null +++ b/include/client.hpp @@ -0,0 +1,7 @@ +#ifdef __SWITCH__ +namespace fs = std::filesystem; +#else +namespace fs = std::__fs::filesystem; +#endif + +int transfer_files(fs::path directory, char** filenames, int file_count); \ No newline at end of file diff --git a/include/common.hpp b/include/common.hpp new file mode 100644 index 0000000..b80a8f6 --- /dev/null +++ b/include/common.hpp @@ -0,0 +1,65 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef COMMON_HPP +#define COMMON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ATEXIT(func) atexit((void (*)())func) + +namespace DateTime { + std::string timeStr(void); + std::string dateTimeStr(void); + std::string logDateTime(void); +} + +namespace StringUtils { + bool containsInvalidChar(const std::string& str); + std::string escapeJson(const std::string& s); + std::string format(const std::string fmt_str, ...); + std::string removeForbiddenCharacters(std::string src); + std::string UTF16toUTF8(const std::u16string& src); + void ltrim(std::string& s); + void rtrim(std::string& s); + void trim(std::string& s); +} + +char* getConsoleIP(void); + +#endif \ No newline at end of file diff --git a/include/data.h b/include/data.h deleted file mode 100644 index 17336de..0000000 --- a/include/data.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -#include - -#include -#include -#include - -#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 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 users; - //Title data/info map - extern std::unordered_map 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; - -} \ No newline at end of file diff --git a/include/directory.hpp b/include/directory.hpp new file mode 100644 index 0000000..eefe165 --- /dev/null +++ b/include/directory.hpp @@ -0,0 +1,58 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef DIRECTORY_HPP +#define DIRECTORY_HPP + +#include +#include +#include +#include +#include + +struct DirectoryEntry { + std::string name; + bool directory; +}; + +class Directory { +public: + Directory(const std::string& root); + ~Directory() = default; + + Result error(void); + std::string entry(size_t index); + bool folder(size_t index); + bool good(void); + size_t size(void); + +private: + std::vector mList; + Result mError; + bool mGood; +}; + +#endif \ No newline at end of file diff --git a/include/filesystem.hpp b/include/filesystem.hpp new file mode 100644 index 0000000..a940a3e --- /dev/null +++ b/include/filesystem.hpp @@ -0,0 +1,39 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef FILESYSTEM_HPP +#define FILESYSTEM_HPP + +#include "account.hpp" +#include + +namespace FileSystem { + Result mount(FsFileSystem* fileSystem, u64 titleID, AccountUid userID); + int mount(FsFileSystem fs); + void unmount(void); +} + +#endif \ No newline at end of file diff --git a/include/fs.h b/include/fs.h deleted file mode 100644 index a582ba5..0000000 --- a/include/fs.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include - -#include -#include - -#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, ...); -} diff --git a/include/fs/dir.h b/include/fs/dir.h deleted file mode 100644 index 8dc83b3..0000000 --- a/include/fs/dir.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#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 item; - }; -} diff --git a/include/fs/file.h b/include/fs/file.h deleted file mode 100644 index 08ed43a..0000000 --- a/include/fs/file.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#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(); -} diff --git a/include/fs/fsfile.h b/include/fs/fsfile.h deleted file mode 100644 index c84be82..0000000 --- a/include/fs/fsfile.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include - -//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 diff --git a/include/fs/fstype.h b/include/fs/fstype.h deleted file mode 100644 index 347f05d..0000000 --- a/include/fs/fstype.h +++ /dev/null @@ -1,46 +0,0 @@ -#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; -} diff --git a/include/fs/ldn.h b/include/fs/ldn.h deleted file mode 100644 index 16b74fb..0000000 --- a/include/fs/ldn.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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); -}; \ No newline at end of file diff --git a/include/fs/zip.h b/include/fs/zip.h deleted file mode 100644 index 4f8afb5..0000000 --- a/include/fs/zip.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include - -#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); -} diff --git a/include/io.hpp b/include/io.hpp new file mode 100644 index 0000000..3693f5b --- /dev/null +++ b/include/io.hpp @@ -0,0 +1,55 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef IO_HPP +#define IO_HPP + +#include "account.hpp" +#include "directory.hpp" +#include "title.hpp" +#include "util.hpp" +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 0x80000 + +namespace io { + std::tuple backup(size_t index, AccountUid uid); + std::tuple restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell); + + Result copyDirectory(const std::string& srcPath, const std::string& dstPath); + void copyFile(const std::string& srcPath, const std::string& dstPath); + Result createDirectory(const std::string& path); + Result deleteFolderRecursively(const std::string& path); + bool directoryExists(const std::string& path); + bool fileExists(const std::string& path); +} + +#endif \ No newline at end of file diff --git a/include/logger.hpp b/include/logger.hpp new file mode 100644 index 0000000..529ac58 --- /dev/null +++ b/include/logger.hpp @@ -0,0 +1,84 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef LOGGER_HPP +#define LOGGER_HPP + +#include "common.hpp" + +#include +#include + +class Logger { +public: + static Logger& getInstance(void) + { + static Logger mLogger; + return mLogger; + } + + inline static const std::string INFO = "[ INFO]"; + inline static const std::string DEBUG = "[DEBUG]"; + inline static const std::string ERROR = "[ERROR]"; + inline static const std::string WARN = "[ WARN]"; + + template + void log(const std::string& level, const std::string& format = {}, Args... args) + { + // xcbuffer += StringUtils::format(("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...); + printf("%s\n", format.c_str()); + // printf("%s\n",StringUtils::format("[" + DateTime::logDateTime() + "] " + level + " " + format + "\n").c_str(), args...); + } + + void flush(void) + { + mFile = fopen(mPath.c_str(), "a"); + if (mFile != NULL) { + fprintf(mFile, buffer.c_str()); + fprintf(stderr, buffer.c_str()); + fclose(mFile); + } + } + +private: + Logger(void) { buffer = ""; } + ~Logger(void) {} + + Logger(Logger const&) = delete; + void operator=(Logger const&) = delete; + +#if defined(__SWITCH__) + const std::string mPath = "/switch/NXST/log.log"; +#else + const std::string mPath = "log.log"; +#endif + + FILE* mFile; + + std::string buffer; +}; + +#endif \ No newline at end of file diff --git a/include/main.hpp b/include/main.hpp new file mode 100644 index 0000000..a46e5d2 --- /dev/null +++ b/include/main.hpp @@ -0,0 +1,26 @@ +#ifndef MAIN_HPP +#define MAIN_HPP +#include +#include "account.hpp" +#include "title.hpp" +#include "util.hpp" +#include +#include +#include "logger.hpp" + +typedef enum { SORT_ALPHA, SORT_LAST_PLAYED, SORT_PLAY_TIME, SORT_MODES_COUNT } sort_t; + +inline float g_currentTime = 0; +inline AccountUid g_currentUId; +inline bool g_backupScrollEnabled = 0; +inline bool g_notificationLedAvailable = false; +inline bool g_shouldExitNetworkLoop = false; +inline std::string g_selectedCheatKey; +inline std::vector g_selectedCheatCodes; +inline u32 g_username_dotsize; +inline sort_t g_sortMode = SORT_ALPHA; +inline std::string g_currentFile = ""; +inline bool g_isTransferringFile = false; +inline const std::string g_emptySave = "New..."; + +#endif \ No newline at end of file diff --git a/include/threads.h b/include/threads.h deleted file mode 100644 index a384904..0000000 --- a/include/threads.h +++ /dev/null @@ -1,44 +0,0 @@ -#include -#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 threads; - uint8_t lgFrame = 0, clrShft = 0; - bool clrAdd = true; - unsigned frameCount = 0; - Mutex threadLock = 0; - }; -} diff --git a/include/title.hpp b/include/title.hpp new file mode 100644 index 0000000..bdada8f --- /dev/null +++ b/include/title.hpp @@ -0,0 +1,90 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef TITLE_HPP +#define TITLE_HPP + +#include "account.hpp" +#include "filesystem.hpp" +#include "io.hpp" +#include +#include +#include +#include +#include +#include +#include + +class Title { +public: + void init(u8 saveDataType, u64 titleid, AccountUid userID, const std::string& name, const std::string& author); + ~Title() = default; + + std::string author(void); + std::pair displayName(void); + u64 id(void); + std::string name(void); + std::string path(void); + u64 playTimeNanoseconds(void); + std::string playTime(void); + void playTimeNanoseconds(u64 playTimeNanoseconds); + u32 lastPlayedTimestamp(void); + void lastPlayedTimestamp(u32 lastPlayedTimestamp); + std::string fullPath(size_t index); + void refreshDirectories(void); + u64 saveId(); + void saveId(u64 id); + std::vector saves(void); + u8 saveDataType(void); + AccountUid userId(void); + std::string userName(void); + +private: + u64 mId; + u64 mSaveId; + AccountUid mUserId; + std::string mUserName; + std::string mName; + std::string mSafeName; + std::string mAuthor; + std::string mPath; + std::vector mSaves; + std::vector mFullSavePaths; + u8 mSaveDataType; + std::pair mDisplayName; + u64 mPlayTimeNanoseconds; + u32 mLastPlayedTimestamp; +}; + +void getTitle(Title& dst, AccountUid uid, size_t i); +size_t getTitleCount(AccountUid uid); +void loadTitles(void); +void sortTitles(void); +void rotateSortMode(void); +void refreshDirectories(u64 id); +std::unordered_map getCompleteTitleList(void); + +#endif \ No newline at end of file diff --git a/include/type.h b/include/type.h deleted file mode 100644 index 4f73986..0000000 --- a/include/type.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include -#include -#include - -//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; diff --git a/include/util.h b/include/util.h deleted file mode 100644 index 72548cc..0000000 --- a/include/util.h +++ /dev/null @@ -1,152 +0,0 @@ -#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); -} \ No newline at end of file diff --git a/include/util.hpp b/include/util.hpp new file mode 100644 index 0000000..1a53a76 --- /dev/null +++ b/include/util.hpp @@ -0,0 +1,52 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#ifndef UTIL_HPP +#define UTIL_HPP + +#include "account.hpp" +#include "common.hpp" +#include "io.hpp" +#include +#include + +// debug +#include +#include +#include + +void servicesExit(void); +Result servicesInit(void); +HidsysNotificationLedPattern blinkLedPattern(u8 times); +void blinkLed(u8 times); + +namespace StringUtils { + std::string removeAccents(std::string str); + std::string removeNotAscii(std::string str); + std::u16string UTF8toUTF16(const char* src); +} + +#endif diff --git a/server.cpp b/server.cpp new file mode 100644 index 0000000..b000adc --- /dev/null +++ b/server.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SWITCH__ +#include +#endif + +#define PORT 8080 +#define BUFFER_SIZE 1024 +#define MULTICAST_PORT 8081 +#define MULTICAST_GROUP "239.0.0.1" // Multicast group IP + +void sendAck(int sock) { + const char *ack = "ACK"; + std::cout << "Sending ACK " << std::endl; + send(sock, ack, strlen(ack), 0); +} + +// Функция для получения файла +void receive_file(std::string dirname, int client_socket) { + char buffer[BUFFER_SIZE] = {0}; + // Receive filename length + uint32_t filename_len; + ssize_t bytes_read = read(client_socket, &filename_len, sizeof(filename_len)); + + std::cout << "Filename length: " << filename_len << std::endl; + + // Check for end-of-transmission signal + if (bytes_read <= 0 || filename_len == 0) { + std::cout << "End of transmission detected." << std::endl; + pthread_exit(nullptr); + } + + // Receive filename + char *filename = new char[filename_len + 1]; + read(client_socket, filename, filename_len); + filename[filename_len] = '\0'; + + std::cout << "Receiving file: " << filename << std::endl; + + // Receive file size + size_t file_size; + read(client_socket, &file_size, sizeof(file_size)); + + std::cout << "With size: " << file_size << std::endl; + // Open a file to write the received data + std::ofstream outfile(dirname + "/" + filename, std::ios::binary); + delete[] filename; // Clean up filename buffer + + size_t total_bytes_received = 0; + while (total_bytes_received < file_size) { + ssize_t bytes_received = read(client_socket, buffer, BUFFER_SIZE); + std::cout << "bytes received: " << bytes_received << std::endl; + if (bytes_received <= 0) { + break; + } + outfile.write(buffer, bytes_received); + total_bytes_received += bytes_received; + + // Send acknowledgment for each chunk received + sendAck(client_socket); + } + + std::cout << "File received successfully." << std::endl; + outfile.close(); +} + +void *handle_client(void *socket_desc) { + int client_socket = *(int *)socket_desc; + free(socket_desc); + + std::cout << "Обработка нового клиента в потоке " << pthread_self() << "\n"; + + // Receive directory length + uint32_t directory_len; + ssize_t bytes_read = read(client_socket, &directory_len, sizeof(directory_len)); + + // Check for end-of-transmission signal + if (bytes_read <= 0 || directory_len == 0) { + std::cout << "End of transmission detected." << std::endl; + pthread_exit(nullptr); + } + + // Receive filename + char *dirname = new char[directory_len + 1]; + read(client_socket, dirname, directory_len); + dirname[directory_len] = '\0'; + + std::cout << "Directory name is " << dirname << std::endl; + + if (!std::filesystem::create_directory(dirname)) { + std::cerr << "Unable to create directory" << std::endl; + pthread_exit(nullptr); + } + + // Получаем файлы до тех пор, пока клиент не закроет соединение + while (true) { + receive_file(dirname, client_socket); + } + + close(client_socket); + pthread_exit(nullptr); +} + +void *broadcast_listener(void *) { + int sockfd; + struct sockaddr_in servaddr; + char buffer[BUFFER_SIZE]; + struct ip_mreq group; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket creation failed"); + pthread_exit(nullptr); + } + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + servaddr.sin_port = htons(MULTICAST_PORT); + + if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) { + perror("binding datagram socket"); + close(sockfd); + pthread_exit(nullptr); + } + + group.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP); + group.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0) { + perror("setsockopt failed"); + close(sockfd); + pthread_exit(nullptr); + } + + std::cout << "Broadcast listener started" << std::endl; + struct sockaddr_in client_addr; + socklen_t addr_len = sizeof(client_addr); + while (true) { + int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &addr_len); + if (n < 0) { + perror("recvfrom failed"); + continue; + } + std::cout << buffer << std::endl; + buffer[n] = '\0'; + if (strcmp(buffer, "DISCOVER_SERVER") == 0) { + const char *message = "SERVER_HERE"; + sendto(sockfd, message, strlen(message), 0, (const struct sockaddr *)&client_addr, addr_len); + std::cout << "Server discovery response sent to multicast group" << std::endl; + } + } + + close(sockfd); + pthread_exit(nullptr); +} + +int main() { +#ifdef __SWITCH__ + socketInitializeDefault(); + nxlinkStdio(); +#endif + pthread_t broadcast_thread; + if (pthread_create(&broadcast_thread, nullptr, broadcast_listener, nullptr) < 0) { + perror("Thread creation failed"); + return 1; + } + + int server_fd, new_socket; + struct sockaddr_in address; + socklen_t addrlen = sizeof(address); + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("Socket creation failed"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(PORT); + + if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { + perror("Bind failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + if (listen(server_fd, 3) < 0) { + perror("Listen failed"); + close(server_fd); + exit(EXIT_FAILURE); + } + + std::cout << "Server listening on port " << PORT << std::endl; + + while (true) { + sockaddr_in client_address; + socklen_t client_len = sizeof(client_address); + int client_socket = accept(server_fd, (sockaddr *)&client_address, &client_len); + + if (client_socket < 0) { + std::cerr << "Ошибка принятия подключения\n"; + continue; + } + + // Создаем новый поток для обработки клиента + pthread_t thread_id; + int *pclient = new (std::nothrow) int(client_socket); + if (!pclient) { + std::cerr << "Ошибка выделения памяти\n"; + close(client_socket); + continue; + } + if (pthread_create(&thread_id, nullptr, handle_client, pclient) != 0) { + std::cerr << "Ошибка создания потока\n"; + delete pclient; // Освобождаем память при ошибке + } else { + pthread_detach(thread_id); // Отсоединяем поток, чтобы он мог завершиться самостоятельно + } + } + + close(server_fd); +#ifdef __SWITCH__ + socketExit(); +#endif + return 0; +} diff --git a/source/.DS_Store b/source/.DS_Store deleted file mode 100644 index 4cf2dca..0000000 Binary files a/source/.DS_Store and /dev/null differ diff --git a/source/Main.cpp b/source/Main.cpp index ac54e21..60393c3 100644 --- a/source/Main.cpp +++ b/source/Main.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include "util.hpp" +#include "main.hpp" static int nxlink_sock = -1; @@ -35,7 +35,14 @@ extern "C" void userAppExit() { // Main entrypoint int main() { - printf("main\n"); + Result res = servicesInit(); + if (R_FAILED(res)) { + servicesExit(); + exit(res); + } + + loadTitles(); + // First create our renderer, where one can customize SDL or other stuff's // initialization. auto renderer_opts = pu::ui::render::RendererInitOptions( @@ -46,8 +53,6 @@ int main() { 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); diff --git a/source/TitlesLayout.cpp b/source/TitlesLayout.cpp index 52822c2..4992a53 100644 --- a/source/TitlesLayout.cpp +++ b/source/TitlesLayout.cpp @@ -1,43 +1,21 @@ #include -#include -#include -#include +#include +#include +#include +#include static std::vector 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()); + for (size_t i = 0; i < getTitleCount(g_currentUId); i++) { + Title title; + getTitle(title, g_currentUId, i); + auto titleItem = pu::ui::elm::MenuItem::New(title.name().c_str()); titleItem->SetColor(COLOR("#FFFFFFFF")); - titlesMenu->AddItem(titleItem); + this->titlesMenu->AddItem(titleItem); } this->Add(this->titlesMenu); @@ -52,13 +30,32 @@ namespace ui { } 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); + auto index = this->titlesMenu->GetSelectedIndex(); + Title title; + getTitle(title, g_currentUId, index); + printf("userid is 0x%lX%lX\n", title.userId().uid[1], title.userId().uid[0]); + printf("current game index is %i\n", index); + int opt = mainApp->CreateShowDialog(title.name().c_str(), "What do you want?", { "Transfer", "Receive" }, true); switch (opt) { case 0: { - // Transfert selected + printf("path is %s\n", title.fullPath(0).c_str()); + // Transfer selected + auto result = io::backup(index, g_currentUId); + if (std::get<0>(result)) { + printf("path is %s\n", std::get<2>(result).c_str()); + std::string path = std::get<2>(result); + std::vector files; + std::vector cstrings{}; + auto directory = std::filesystem::path(path); + for (const auto & entry : std::filesystem::directory_iterator(path)) { + std::cout << entry.path() << std::endl; + files.push_back(entry.path().string()); + } + for (auto& file : files) { + cstrings.push_back(&file.front()); + } + transfer_files(directory, cstrings.data(), cstrings.size()); + } break; } case 1: { diff --git a/source/UsersLayout.cpp b/source/UsersLayout.cpp index 7c5629c..f4cb43e 100644 --- a/source/UsersLayout.cpp +++ b/source/UsersLayout.cpp @@ -1,17 +1,16 @@ #include -#include #include +#include "main.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 = 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())); + for (AccountUid const& uid : Account::ids()) { + auto username = pu::ui::elm::MenuItem::New(Account::username(uid) + ": " + std::to_string(getTitleCount(uid))); username->SetColor(COLOR("#FFFFFFFF")); this->usersMenu->AddItem(username); } @@ -31,7 +30,7 @@ namespace ui { if (Down & HidNpadButton_A) { printf("current index is %i\n", this->usersMenu->GetSelectedIndex()); - data::setUserIndex(this->usersMenu->GetSelectedIndex()); + g_currentUId = Account::ids().at(this->usersMenu->GetSelectedIndex()); mainApp->titlesLayout->InitTitles(); mainApp->LoadLayout(mainApp->titlesLayout); } diff --git a/source/account.cpp b/source/account.cpp new file mode 100644 index 0000000..37dc212 --- /dev/null +++ b/source/account.cpp @@ -0,0 +1,95 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "account.hpp" +#include + +static std::map mUsers; + +Result Account::init(void) +{ + return accountInitialize(AccountServiceType_Application); +} + +void Account::exit(void) +{ + accountExit(); +} + +std::vector Account::ids(void) +{ + std::vector v; + for (auto& value : mUsers) { + v.push_back(value.second.id); + } + return v; +} + +static User getUser(AccountUid id) +{ + User user{id, ""}; + AccountProfile profile; + AccountProfileBase profilebase; + memset(&profilebase, 0, sizeof(profilebase)); + + if (R_SUCCEEDED(accountGetProfile(&profile, id)) && R_SUCCEEDED(accountProfileGet(&profile, NULL, &profilebase))) { + user.name = std::string(profilebase.nickname, 0x20); + } + + accountProfileClose(&profile); + return user; +} + +std::string Account::username(AccountUid id) +{ + std::map::const_iterator got = mUsers.find(id); + if (got == mUsers.end()) { + User user = getUser(id); + mUsers.insert({id, user}); + return user.name; + } + + return got->second.name; +} + +AccountUid Account::selectAccount(void) +{ + LibAppletArgs args; + libappletArgsCreate(&args, 0x10000); + u8 st_in[0xA0] = {0}; + u8 st_out[0x18] = {0}; + size_t repsz; + + Result res = libappletLaunch(AppletId_LibraryAppletPlayerSelect, &args, st_in, 0xA0, st_out, 0x18, &repsz); + if (R_SUCCEEDED(res)) { + u64 lres = *(u64*)st_out; + AccountUid uid = *(AccountUid*)&st_out[8]; + if (lres == 0) + return uid; + } + + return g_currentUId; +} \ No newline at end of file diff --git a/source/client.cpp b/source/client.cpp new file mode 100644 index 0000000..6c3d9cd --- /dev/null +++ b/source/client.cpp @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __SWITCH__ +#include +#include +namespace fs = std::filesystem; +#else +namespace fs = std::__fs::filesystem; +#endif +#define PORT 8080 +#define BUFFER_SIZE 1024 +#define MULTICAST_PORT 8081 +#define MULTICAST_GROUP "239.0.0.1" // Multicast group IP + +struct ThreadArgs +{ + char **filenames; + int file_count; + int sock; + fs::path directory; +}; + +bool receiveAck(int sock) +{ + char ack[4] = {0}; + int bytes_received = read(sock, ack, 3); + return bytes_received > 0 && std::string(ack) == "ACK"; +} + +// Отправка строки с учетом её длины +void send_string(int sock, const std::string &str) +{ + size_t length = str.size(); + send(sock, &length, sizeof(length), 0); + send(sock, str.c_str(), length, 0); +} + +void *send_files_thread(void *args) +{ + ThreadArgs *thread_args = static_cast(args); + char **filenames = thread_args->filenames; + int file_count = thread_args->file_count; + int sock = thread_args->sock; + fs::path cwd = thread_args->directory; + delete thread_args; + + char buffer[BUFFER_SIZE]; + + // Send the directory length + + std::cout << Logger::INFO << "cwd.filename is: " << cwd.filename().c_str() << std::endl; // Get the parent directory + std::string dirname = cwd.parent_path().filename(); + uint32_t directory_len = dirname.size(); + send(sock, &directory_len, sizeof(directory_len), 0); + + // Send the dirname + send(sock, dirname.c_str(), directory_len, 0); + + for (int i = 0; i < file_count; ++i) + { + std::string path = filenames[i]; + std::size_t found = path.find_last_of("/\\"); + std::string filename = path.substr(found+1); + std::ifstream infile(path, std::ios::binary | std::ios::ate); + if (!infile.is_open()) + { + std::cout << Logger::ERROR << "File not found: " << filename.c_str() << std::endl; + return nullptr; + } + + // Get the size of the file + std::streamsize file_size = infile.tellg(); + infile.seekg(0, std::ios::beg); + + // Send the filename length + uint32_t filename_len = filename.size(); + std::cout << Logger::INFO << "Send filename length: " << filename_len << std::endl; + send(sock, &filename_len, sizeof(filename_len), 0); + + // Send the filename + std::cout << Logger::INFO << "Send file name: " << filename.c_str() << std::endl; + send(sock, filename.c_str(), filename_len, 0); + + // Send the file size + std::cout << Logger::INFO << "Send file size: " << file_size << std::endl; + send(sock, &file_size, sizeof(file_size), 0); + + char buffer[BUFFER_SIZE]; + // Send the file data + while (file_size > 0) + { + infile.read(buffer, BUFFER_SIZE); + send(sock, buffer, infile.gcount(), 0); + file_size -= infile.gcount(); + std::cout << Logger::INFO << "wait for ACK" << std::endl; + // Wait for acknowledgment after each chunk + if (!receiveAck(sock)) + { + std::cout << Logger::ERROR << "Failed to receive acknowledgment" << std::endl;; + } + } + + send(sock, 0, 0, 0); + + std::cout << Logger::INFO << "File sent successfully: " << filename.c_str() << std::endl; + + infile.close(); + } + + close(sock); + pthread_exit(nullptr); +} + +int find_server(char *server_ip) +{ + std::cout << Logger::INFO << "Init find_server" << std::endl;; + int sockfd; + struct sockaddr_in multicast_addr; + + std::cout << Logger::INFO << "Create socket" << std::endl; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + std::cout << Logger::ERROR << "Socket creation error" << std::endl; + return -1; + } + + memset(&multicast_addr, 0, sizeof(multicast_addr)); + + multicast_addr.sin_family = AF_INET; + multicast_addr.sin_port = htons(MULTICAST_PORT); + multicast_addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP); + + std::cout << Logger::INFO << "Send DISCOVER_SERVER" << std::endl; + + const char *multicast_message = "DISCOVER_SERVER"; + if (sendto(sockfd, multicast_message, strlen(multicast_message), 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)) < 0) + { + std::cout << Logger::ERROR << "sendto failed" << std::endl; + close(sockfd); + return -1; + } + else + { + std::cout << Logger::INFO << "send multicast message success" << std::endl; + } + + struct sockaddr_in cliaddr; + socklen_t len = sizeof(cliaddr); + char buffer[BUFFER_SIZE]; + + ssize_t n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, &len); + std::cout << Logger::INFO << "recvfrom n: %i" << n << std::endl; + if (n < 0) + { + std::cout << Logger::ERROR << "recvfrom failed" << std::endl; + close(sockfd); + return -1; + } + std::cout << Logger::INFO << "buffer: " << buffer << std::endl; + buffer[n] = '\0'; + if (strcmp(buffer, "SERVER_HERE") == 0) + { + std::cout << Logger::INFO << "Server found" << std::endl; + inet_ntop(AF_INET, &cliaddr.sin_addr, server_ip, INET_ADDRSTRLEN); + } + else + { + std::cout << Logger::ERROR << "Unable to find server, close socket" << std::endl;; + close(sockfd); + return -1; + } + + close(sockfd); + return 0; +} + +int transfer_files(fs::path directory, char **filenames, int file_count) { + std::cout << Logger::INFO << "Init transfer_files" << std::endl; + char server_ip[INET_ADDRSTRLEN]; + if (find_server(server_ip) != 0) + { + std::cout << Logger::ERROR << "Failed to find server" << std::endl; + return -1; + } + + int sock = 0; + struct sockaddr_in serv_addr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + std::cout << Logger::ERROR << "Socket creation error" << std::endl; + return -1; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) + { + std::cout << Logger::ERROR << "Invalid address / Address not supported" << std::endl; + return -1; + } + + if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) + { + std::cout << Logger::ERROR << "Connection failed" << std::endl; + return -1; + } + + pthread_t file_thread; + ThreadArgs *thread_args = new ThreadArgs{filenames, file_count, sock, directory}; + if (pthread_create(&file_thread, nullptr, send_files_thread, + (void *)thread_args) < 0) + { + std::cout << Logger::ERROR << "Thread creation failed" << std::endl; + close(sock); + delete thread_args; + return -1; + } + else + { + std::cout << Logger::INFO << "Wait for file_thread" << std::endl; + pthread_join(file_thread, nullptr); + } + return 0; +} + +#ifndef __SWITCH__ // for desktop +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + std::cerr << "Usage: " << argv[0] << " ..." << std::endl; + return 1; + } + + char **filenames = const_cast(&argv[1]); + int file_count = argc - 1; + + transfer_files(filenames, file_count); + + return 0; +} +#endif diff --git a/source/common.cpp b/source/common.cpp new file mode 100644 index 0000000..dbd2cea --- /dev/null +++ b/source/common.cpp @@ -0,0 +1,124 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "common.hpp" + +std::string DateTime::timeStr(void) +{ + time_t unixTime; + struct tm timeStruct; + time(&unixTime); + localtime_r(&unixTime, &timeStruct); + return StringUtils::format("%02i:%02i:%02i", timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec); +} + +std::string DateTime::dateTimeStr(void) +{ + time_t unixTime; + struct tm timeStruct; + time(&unixTime); + localtime_r(&unixTime, &timeStruct); + return StringUtils::format("%04i%02i%02i-%02i%02i%02i", timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday, timeStruct.tm_hour, + timeStruct.tm_min, timeStruct.tm_sec); +} + +std::string DateTime::logDateTime(void) +{ + time_t unixTime; + struct tm timeStruct; + time(&unixTime); + localtime_r(&unixTime, &timeStruct); + return StringUtils::format("%04i-%02i-%02i %02i:%02i:%02i", timeStruct.tm_year + 1900, timeStruct.tm_mon + 1, timeStruct.tm_mday, + timeStruct.tm_hour, timeStruct.tm_min, timeStruct.tm_sec); +} + +std::string StringUtils::UTF16toUTF8(const std::u16string& src) +{ + static std::wstring_convert, char16_t> convert; + std::string dst = convert.to_bytes(src); + return dst; +} + +std::string StringUtils::removeForbiddenCharacters(std::string src) +{ + static const std::string illegalChars = ".,!\\/:?*\"<>|"; + for (size_t i = 0, sz = src.length(); i < sz; i++) { + if (illegalChars.find(src[i]) != std::string::npos) { + src[i] = ' '; + } + } + + size_t i; + for (i = src.length() - 1; i > 0 && src[i] == ' '; i--) + ; + src.erase(i + 1, src.length() - i); + + return src; +} + +std::string StringUtils::format(const std::string fmt_str, ...) +{ + va_list ap; + char* fp = NULL; + va_start(ap, fmt_str); + vasprintf(&fp, fmt_str.c_str(), ap); + va_end(ap); + std::unique_ptr formatted(fp); + return std::string(formatted.get()); +} + +bool StringUtils::containsInvalidChar(const std::string& str) +{ + for (size_t i = 0, sz = str.length(); i < sz; i++) { + if (!isascii(str[i])) { + return true; + } + } + return false; +} + +void StringUtils::ltrim(std::string& s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); +} + +void StringUtils::rtrim(std::string& s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); +} + +void StringUtils::trim(std::string& s) +{ + ltrim(s); + rtrim(s); +} + +char* getConsoleIP(void) +{ + struct in_addr in; + in.s_addr = gethostid(); + return inet_ntoa(in); +} \ No newline at end of file diff --git a/source/data.cpp b/source/data.cpp deleted file mode 100644 index 9048f8d..0000000 --- a/source/data.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#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::users; - -//For other save types -static bool sysBCATPushed = false, tempPushed = false; -std::unordered_map 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"; - } -} diff --git a/source/directory.cpp b/source/directory.cpp new file mode 100644 index 0000000..6fbd197 --- /dev/null +++ b/source/directory.cpp @@ -0,0 +1,77 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "directory.hpp" + +Directory::Directory(const std::string& root) +{ + mGood = false; + mError = 0; + mList.clear(); + + DIR* dir = opendir(root.c_str()); + struct dirent* ent; + + if (dir == NULL) { + mError = (Result)errno; + } + else { + while ((ent = readdir(dir))) { + std::string name = std::string(ent->d_name); + bool directory = ent->d_type == DT_DIR; + struct DirectoryEntry de = {name, directory}; + mList.push_back(de); + } + } + + closedir(dir); + mGood = true; +} + +Result Directory::error(void) +{ + return mError; +} + +bool Directory::good(void) +{ + return mGood; +} + +std::string Directory::entry(size_t index) +{ + return index < mList.size() ? mList.at(index).name : ""; +} + +bool Directory::folder(size_t index) +{ + return index < mList.size() ? mList.at(index).directory : false; +} + +size_t Directory::size(void) +{ + return mList.size(); +} \ No newline at end of file diff --git a/source/filesystem.cpp b/source/filesystem.cpp new file mode 100644 index 0000000..7df86a1 --- /dev/null +++ b/source/filesystem.cpp @@ -0,0 +1,42 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "filesystem.hpp" + +Result FileSystem::mount(FsFileSystem* fileSystem, u64 titleID, AccountUid userID) +{ + return fsOpen_SaveData(fileSystem, titleID, userID); +} + +int FileSystem::mount(FsFileSystem fs) +{ + return fsdevMountDevice("save", fs); +} + +void FileSystem::unmount(void) +{ + fsdevUnmountDevice("save"); +} \ No newline at end of file diff --git a/source/fs.cpp b/source/fs.cpp deleted file mode 100644 index 13df28e..0000000 --- a/source/fs.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include - -#include "fs.h" -#include "threads.h" -#include "util.h" - -static std::string wd = "sdmc:/NXST/"; - -static FSFILE *debLog; - -static FsFileSystem sv; - -static std::vector 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); -} - diff --git a/source/fs/dir.cpp b/source/fs/dir.cpp deleted file mode 100644 index 20b3164..0000000 --- a/source/fs/dir.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include -#include - -#include "fs.h" -#include "util.h" -#include - -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); -} diff --git a/source/fs/file.cpp b/source/fs/file.cpp deleted file mode 100644 index 4911d7d..0000000 --- a/source/fs/file.cpp +++ /dev/null @@ -1,383 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fs.h" -#include "util.h" -#include "data.h" -#include - -static std::string wd = "sdmc:/JKSV/"; - -typedef struct -{ - std::mutex bufferLock; - std::condition_variable cond; - std::vector 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 localBuffer; - std::FILE *out = std::fopen(in->dst.c_str(), "wb"); - - while(written < in->filesize) - { - std::unique_lock 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 localBuffer; - FILE *out = fopen(in->dst.c_str(), "wb"); - - while(written < in->filesize) - {` - std::unique_lock 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 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 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 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 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; -} diff --git a/source/fs/fsfile.c b/source/fs/fsfile.c deleted file mode 100644 index d0b7d52..0000000 --- a/source/fs/fsfile.c +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include -#include - -#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; -} diff --git a/source/fs/zip.cpp b/source/fs/zip.cpp deleted file mode 100644 index 1795b2b..0000000 --- a/source/fs/zip.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include -#include -#include -#include -#include - -#include "fs.h" -#include "util.h" -#include "threads.h" - -typedef struct -{ - std::mutex buffLock; - std::condition_variable cond; - std::vector 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 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 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 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 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; -} diff --git a/source/io.cpp b/source/io.cpp new file mode 100644 index 0000000..360b78f --- /dev/null +++ b/source/io.cpp @@ -0,0 +1,269 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "io.hpp" +#include "main.hpp" +#include + +bool io::fileExists(const std::string& path) +{ + struct stat buffer; + return (stat(path.c_str(), &buffer) == 0); +} + +void io::copyFile(const std::string& srcPath, const std::string& dstPath) +{ + g_isTransferringFile = true; + + FILE* src = fopen(srcPath.c_str(), "rb"); + if (src == NULL) { + Logger::getInstance().log(Logger::ERROR, "Failed to open source file " + srcPath + " during copy with errno %d. Skipping...", errno); + return; + } + FILE* dst = fopen(dstPath.c_str(), "wb"); + if (dst == NULL) { + Logger::getInstance().log(Logger::ERROR, "Failed to open destination file " + dstPath + " during copy with errno %d. Skipping...", errno); + fclose(src); + return; + } + + fseek(src, 0, SEEK_END); + u64 sz = ftell(src); + rewind(src); + + u8* buf = new u8[BUFFER_SIZE]; + u64 offset = 0; + + size_t slashpos = srcPath.rfind("/"); + g_currentFile = srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1); + + while (offset < sz) { + u32 count = fread((char*)buf, 1, BUFFER_SIZE, src); + fwrite((char*)buf, 1, count, dst); + offset += count; + } + + delete[] buf; + fclose(src); + fclose(dst); + + // commit each file to the save + if (dstPath.rfind("save:/", 0) == 0) { + Logger::getInstance().log(Logger::ERROR, "Committing file " + dstPath + " to the save archive."); + fsdevCommitDevice("save"); + } + + g_isTransferringFile = false; +} + +Result io::copyDirectory(const std::string& srcPath, const std::string& dstPath) +{ + Result res = 0; + bool quit = false; + Directory items(srcPath); + + if (!items.good()) { + return items.error(); + } + + for (size_t i = 0, sz = items.size(); i < sz && !quit; i++) { + std::string newsrc = srcPath + items.entry(i); + std::string newdst = dstPath + items.entry(i); + + if (items.folder(i)) { + res = io::createDirectory(newdst); + if (R_SUCCEEDED(res)) { + newsrc += "/"; + newdst += "/"; + res = io::copyDirectory(newsrc, newdst); + } + else { + quit = true; + } + } + else { + io::copyFile(newsrc, newdst); + } + } + + return 0; +} + +Result io::createDirectory(const std::string& path) +{ + mkdir(path.c_str(), 777); + return 0; +} + +bool io::directoryExists(const std::string& path) +{ + struct stat sb; + return (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)); +} + +Result io::deleteFolderRecursively(const std::string& path) +{ + Directory dir(path); + if (!dir.good()) { + return dir.error(); + } + + for (size_t i = 0, sz = dir.size(); i < sz; i++) { + if (dir.folder(i)) { + std::string newpath = path + "/" + dir.entry(i) + "/"; + deleteFolderRecursively(newpath); + newpath = path + dir.entry(i); + rmdir(newpath.c_str()); + } + else { + std::string newpath = path + dir.entry(i); + std::remove(newpath.c_str()); + } + } + + rmdir(path.c_str()); + return 0; +} + +std::tuple io::backup(size_t index, AccountUid uid) +{ + Result res = 0; + std::tuple ret = std::make_tuple(false, -1, ""); + Title title; + getTitle(title, uid, index); + + Logger::getInstance().log(Logger::INFO, "Started backup of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), + title.userId().uid[1], title.userId().uid[0]); + + FsFileSystem fileSystem; + res = FileSystem::mount(&fileSystem, title.id(), title.userId()); + if (R_SUCCEEDED(res)) { + int rc = FileSystem::mount(fileSystem); + if (rc == -1) { + FileSystem::unmount(); + Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during backup. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), + title.userId().uid[1], title.userId().uid[0]); + return std::make_tuple(false, -2, "Failed to mount save."); + } + } + else { + Logger::getInstance().log(Logger::ERROR, + "Failed to mount filesystem during backup with result 0x%08lX. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), + title.userId().uid[1], title.userId().uid[0]); + return std::make_tuple(false, res, "Failed to mount save."); + } + + std::string suggestion = StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(title.userId()))); + + std::string dstPath = title.path() + "/" + suggestion; + + if (io::directoryExists(dstPath)) { + int rc = io::deleteFolderRecursively((dstPath + "/").c_str()); + if (rc != 0) { + FileSystem::unmount(); + Logger::getInstance().log(Logger::ERROR, "Failed to recursively delete directory " + dstPath + " with result %d.", rc); + return std::make_tuple(false, (Result)rc, "Failed to delete the existing backup\ndirectory recursively."); + } + } + + io::createDirectory(dstPath); + res = io::copyDirectory("save:/", dstPath + "/"); + if (R_FAILED(res)) { + FileSystem::unmount(); + io::deleteFolderRecursively((dstPath + "/").c_str()); + Logger::getInstance().log(Logger::ERROR, "Failed to copy directory " + dstPath + " with result 0x%08lX. Skipping...", res); + return std::make_tuple(false, res, "Failed to backup save."); + } + + refreshDirectories(title.id()); + + FileSystem::unmount(); + + ret = std::make_tuple(true, 0, dstPath); + Logger::getInstance().log(Logger::INFO, "Backup succeeded."); + return ret; +} + +std::tuple io::restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell) +{ + Result res = 0; + std::tuple ret = std::make_tuple(false, -1, ""); + Title title; + getTitle(title, uid, index); + + Logger::getInstance().log(Logger::INFO, "Started restore of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), + title.userId().uid[1], title.userId().uid[0]); + + FsFileSystem fileSystem; + res = FileSystem::mount(&fileSystem, title.id(), title.userId()); + if (R_SUCCEEDED(res)) { + int rc = FileSystem::mount(fileSystem); + if (rc == -1) { + FileSystem::unmount(); + Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during restore. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), + title.userId().uid[1], title.userId().uid[0]); + return std::make_tuple(false, -2, "Failed to mount save."); + } + } + else { + Logger::getInstance().log(Logger::ERROR, + "Failed to mount filesystem during restore with result 0x%08lX. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), + title.userId().uid[1], title.userId().uid[0]); + return std::make_tuple(false, res, "Failed to mount save."); + } + + std::string srcPath = title.fullPath(cellIndex) + "/"; + std::string dstPath = "save:/"; + + res = io::deleteFolderRecursively(dstPath.c_str()); + if (R_FAILED(res)) { + FileSystem::unmount(); + Logger::getInstance().log(Logger::ERROR, "Failed to recursively delete directory " + dstPath + " with result 0x%08lX.", res); + return std::make_tuple(false, res, "Failed to delete save."); + } + + res = io::copyDirectory(srcPath, dstPath); + if (R_FAILED(res)) { + FileSystem::unmount(); + Logger::getInstance().log(Logger::ERROR, "Failed to copy directory " + srcPath + " to " + dstPath + " with result 0x%08lX. Skipping...", res); + return std::make_tuple(false, res, "Failed to restore save."); + } + + res = fsdevCommitDevice("save"); + if (R_FAILED(res)) { + Logger::getInstance().log(Logger::ERROR, "Failed to commit save with result 0x%08lX.", res); + return std::make_tuple(false, res, "Failed to commit to save device."); + } + else { + blinkLed(4); + ret = std::make_tuple(true, 0, nameFromCell + "\nhas been restored successfully."); + } + + FileSystem::unmount(); + + Logger::getInstance().log(Logger::INFO, "Restore succeeded."); + return ret; +} \ No newline at end of file diff --git a/source/ldn.cpp b/source/ldn.cpp deleted file mode 100644 index 5001df0..0000000 --- a/source/ldn.cpp +++ /dev/null @@ -1,543 +0,0 @@ -#include "ldn.h" - -#include -#include -#include -#include -#include -#include - -#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 localBuffer; - - int fSocket = LDN::bindClient(comm->serverFD); - if (fSocket < 0) - return; - - while (written < in->filesize) { - std::unique_lock 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 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 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 localBuffer; - FILE *out = fopen(in->dst.c_str(), "wb"); - - while (written < in->filesize) { - std::unique_lock 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 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 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); - } -} \ No newline at end of file diff --git a/source/threads.cpp b/source/threads.cpp deleted file mode 100644 index 7a29759..0000000 --- a/source/threads.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#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); -} diff --git a/source/title.cpp b/source/title.cpp new file mode 100644 index 0000000..5745f9f --- /dev/null +++ b/source/title.cpp @@ -0,0 +1,333 @@ +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "title.hpp" +#include "main.hpp" + +static std::unordered_map> titles; + +void Title::init(u8 saveDataType, u64 id, AccountUid userID, const std::string& name, const std::string& author) +{ + mId = id; + mUserId = userID; + mSaveDataType = saveDataType; + mUserName = Account::username(userID); + mAuthor = author; + mName = name; + mSafeName = StringUtils::containsInvalidChar(name) ? StringUtils::format("0x%016llX", mId) : StringUtils::removeForbiddenCharacters(name); + mPath = "sdmc:/switch/NXST/saves/" + StringUtils::format("0x%016llX", mId) + " " + mSafeName; + + std::string aname = StringUtils::removeAccents(mName); + size_t pos = aname.rfind(":"); + mDisplayName = std::make_pair(aname, ""); + if (pos != std::string::npos) { + std::string name1 = aname.substr(0, pos); + std::string name2 = aname.substr(pos + 1); + StringUtils::trim(name1); + StringUtils::trim(name2); + mDisplayName.first = name1; + mDisplayName.second = name2; + } + else { + // check for parenthesis + size_t pos1 = aname.rfind("("); + size_t pos2 = aname.rfind(")"); + if (pos1 != std::string::npos && pos2 != std::string::npos) { + std::string name1 = aname.substr(0, pos1); + std::string name2 = aname.substr(pos1 + 1, pos2 - 1 - pos1); + StringUtils::trim(name1); + StringUtils::trim(name2); + mDisplayName.first = name1; + mDisplayName.second = name2; + } + } + + if (!io::directoryExists(mPath)) { + io::createDirectory(mPath); + } + + refreshDirectories(); +} + +u8 Title::saveDataType(void) +{ + return mSaveDataType; +} + +u64 Title::id(void) +{ + return mId; +} + +u64 Title::saveId(void) +{ + return mSaveId; +} + +void Title::saveId(u64 saveId) +{ + mSaveId = saveId; +} + +AccountUid Title::userId(void) +{ + return mUserId; +} + +std::string Title::userName(void) +{ + return mUserName; +} + +std::string Title::author(void) +{ + return mAuthor; +} + +std::string Title::name(void) +{ + return mName; +} + +std::pair Title::displayName(void) +{ + return mDisplayName; +} + +std::string Title::path(void) +{ + return mPath; +} + +std::string Title::fullPath(size_t index) +{ + return mFullSavePaths.at(index); +} + +std::vector Title::saves() +{ + return mSaves; +} + +u64 Title::playTimeNanoseconds(void) +{ + return mPlayTimeNanoseconds; +} + +std::string Title::playTime(void) +{ + const u64 playTimeMinutes = mPlayTimeNanoseconds / 60000000000; + return StringUtils::format("%d", playTimeMinutes / 60) + ":" + StringUtils::format("%02d", playTimeMinutes % 60) + " hours"; +} + +void Title::playTimeNanoseconds(u64 playTimeNanoseconds) +{ + mPlayTimeNanoseconds = playTimeNanoseconds; +} + +u32 Title::lastPlayedTimestamp(void) +{ + return mLastPlayedTimestamp; +} + +void Title::lastPlayedTimestamp(u32 lastPlayedTimestamp) +{ + mLastPlayedTimestamp = lastPlayedTimestamp; +} + +void Title::refreshDirectories(void) +{ + mSaves.clear(); + mFullSavePaths.clear(); + + Directory savelist(mPath); + if (savelist.good()) { + for (size_t i = 0, sz = savelist.size(); i < sz; i++) { + if (savelist.folder(i)) { + mSaves.push_back(savelist.entry(i)); + mFullSavePaths.push_back(mPath + "/" + savelist.entry(i)); + } + } + + std::sort(mSaves.rbegin(), mSaves.rend()); + std::sort(mFullSavePaths.rbegin(), mFullSavePaths.rend()); + mSaves.insert(mSaves.begin(), g_emptySave); + mFullSavePaths.insert(mFullSavePaths.begin(), g_emptySave); + } + else { + Logger::getInstance().log(Logger::ERROR, "Couldn't retrieve the extdata directory list for the title " + name()); + } + + // save backups from configuration + // std::vector additionalFolders = Configuration::getInstance().additionalSaveFolders(mId); + // for (std::vector::const_iterator it = additionalFolders.begin(); it != additionalFolders.end(); ++it) { + // we have other folders to parse + // Directory list(*it); + // if (list.good()) { + // for (size_t i = 0, sz = list.size(); i < sz; i++) { + // if (list.folder(i)) { + // mSaves.push_back(list.entry(i)); + // mFullSavePaths.push_back(*it + "/" + list.entry(i)); + // } + // } + // } + // } +} + +void loadTitles(void) +{ + titles.clear(); + + FsSaveDataInfoReader reader; + FsSaveDataInfo info; + s64 total_entries = 0; + size_t outsize = 0; + + NacpLanguageEntry* nle = NULL; + NsApplicationControlData* nsacd = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData)); + if (nsacd == NULL) { + return; + } + memset(nsacd, 0, sizeof(NsApplicationControlData)); + + Result res = fsOpenSaveDataInfoReader(&reader, FsSaveDataSpaceId_User); + if (R_FAILED(res)) { + free(nsacd); + return; + } + + while (1) { + res = fsSaveDataInfoReaderRead(&reader, &info, 1, &total_entries); + if (R_FAILED(res) || total_entries == 0) { + break; + } + + if (info.save_data_type == FsSaveDataType_Account) { + u64 tid = info.application_id; + u64 sid = info.save_data_id; + AccountUid uid = info.uid; + // if (mFilterIds.find(tid) == mFilterIds.end()) { + res = nsGetApplicationControlData(NsApplicationControlSource_Storage, tid, nsacd, sizeof(NsApplicationControlData), &outsize); + if (R_SUCCEEDED(res) && !(outsize < sizeof(nsacd->nacp))) { + res = nacpGetLanguageEntry(&nsacd->nacp, &nle); + if (R_SUCCEEDED(res) && nle != NULL) { + Title title; + title.init(info.save_data_type, tid, uid, std::string(nle->name), std::string(nle->author)); + title.saveId(sid); + + // load play statistics + PdmPlayStatistics stats; + res = pdmqryQueryPlayStatisticsByApplicationIdAndUserAccountId(tid, uid, false, &stats); + if (R_SUCCEEDED(res)) { + title.playTimeNanoseconds(stats.playtime); + title.lastPlayedTimestamp(stats.last_timestamp_user); + } + + // loadIcon(tid, nsacd, outsize - sizeof(nsacd->nacp)); + + // check if the vector is already created + std::unordered_map>::iterator it = titles.find(uid); + if (it != titles.end()) { + // found + it->second.push_back(title); + } + else { + // not found, insert into map + std::vector v; + v.push_back(title); + titles.emplace(uid, v); + } + } + } + nle = NULL; + // } + } + } + + free(nsacd); + fsSaveDataInfoReaderClose(&reader); + + sortTitles(); +} + +void sortTitles(void) +{ + for (auto& vect : titles) { + std::sort(vect.second.begin(), vect.second.end(), [](Title& l, Title& r) { + switch (g_sortMode) { + case SORT_LAST_PLAYED: + return l.lastPlayedTimestamp() > r.lastPlayedTimestamp(); + case SORT_PLAY_TIME: + return l.playTimeNanoseconds() > r.playTimeNanoseconds(); + case SORT_ALPHA: + default: + return l.name() < r.name(); + } + }); + } +} + +void rotateSortMode(void) +{ + g_sortMode = static_cast<sort_t>((g_sortMode + 1) % SORT_MODES_COUNT); + sortTitles(); +} + +void getTitle(Title& dst, AccountUid uid, size_t i) +{ + std::unordered_map<AccountUid, std::vector<Title>>::iterator it = titles.find(uid); + if (it != titles.end() && i < getTitleCount(uid)) { + dst = it->second.at(i); + } +} + +size_t getTitleCount(AccountUid uid) +{ + std::unordered_map<AccountUid, std::vector<Title>>::iterator it = titles.find(uid); + return it != titles.end() ? it->second.size() : 0; +} + +void refreshDirectories(u64 id) +{ + for (auto& pair : titles) { + for (size_t i = 0; i < pair.second.size(); i++) { + if (pair.second.at(i).id() == id) { + pair.second.at(i).refreshDirectories(); + } + } + } +} + +std::unordered_map<std::string, std::string> getCompleteTitleList(void) +{ + std::unordered_map<std::string, std::string> map; + for (const auto& pair : titles) { + for (auto value : pair.second) { + map.insert({StringUtils::format("0x%016llX", value.id()), value.name()}); + } + } + return map; +} diff --git a/source/util.cpp b/source/util.cpp index f8dd4fb..f65e0ad 100644 --- a/source/util.cpp +++ b/source/util.cpp @@ -1,351 +1,169 @@ -#include <string> -#include <cstdio> -#include <ctime> -#include <sys/stat.h> -#include <json-c/json.h> +/* + * This file is part of Checkpoint + * Copyright (C) 2017-2021 Bernardo Giordano, FlagBrew + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ -#include "fs/file.h" -#include "data.h" -#include "util.h" -#include "type.h" +#include "util.hpp" +#include <logger.hpp> +#include <MainApplication.hpp> +#include "main.hpp" -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) +void servicesExit(void) { - for(unsigned i = 0; i < 13; i++) - { - if(t == verboten[i]) - return true; + Logger::getInstance().flush(); + pdmqryExit(); + socketExit(); + Account::exit(); + nsExit(); + plExit(); + romfsExit(); +} + +Result servicesInit(void) +{ + io::createDirectory("sdmc:/switch"); + io::createDirectory("sdmc:/switch/NXST"); + io::createDirectory("sdmc:/switch/NXST/saves"); + + Logger::getInstance().log(Logger::INFO, "Starting Checkpoint loading..."); + + if (appletGetAppletType() != AppletType_Application) { + Logger::getInstance().log(Logger::WARN, "Please do not run Checkpoint in applet mode."); } - return false; -} + // Result socinit = 0; + // if ((socinit = socketInitializeDefault()) == 0) { + // nxlinkStdio(); + // } + // else { + // Logger::getInstance().log(Logger::INFO, "Unable to initialize socket. Result code 0x%08lX.", socinit); + // } -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); -} + // g_shouldExitNetworkLoop = R_FAILED(socinit); -//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); -} + Result res = 0; -//Missing swkbd config funcs for now -typedef enum -{ - SwkbdPosStart = 0, - SwkbdPosEnd = 1 -} SwkbdInitPos; + romfsInit(); -typedef struct -{ - uint16_t read[0x32 / sizeof(uint16_t)]; - uint16_t word[0x32 / sizeof(uint16_t)]; -} dictWord; + padConfigureInput(1, HidNpadStyleSet_NpadStandard); + hidInitializeTouchScreen(); -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; + if (R_FAILED(res = plInitialize(PlServiceType_User))) { + Logger::getInstance().log(Logger::ERROR, "plInitialize failed. Result code 0x%08lX.", res); + return res; } - 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; + if (R_FAILED(res = Account::init())) { + Logger::getInstance().log(Logger::ERROR, "Account::init failed. Result code 0x%08lX.", res); + return res; } - 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; + if (R_FAILED(res = nsInitialize())) { + Logger::getInstance().log(Logger::ERROR, "nsInitialize failed. Result code 0x%08lX.", res); + return res; } - //Check for spaces at end - while(ret[ret.length() - 1] == ' ' || ret[ret.length() - 1] == '.') - ret.erase(ret.length() - 1, 1); + // Configuration::getInstance(); - 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); + if (R_SUCCEEDED(res = pdmqryInitialize())) {} + else { + Logger::getInstance().log(Logger::WARN, "pdmqryInitialize failed with result 0x%08lX.", res); } - char out[maxLength + 1]; - memset(out, 0, maxLength + 1); - swkbdShow(&swkbd, out, maxLength + 1); - swkbdClose(&swkbd); + Logger::getInstance().log(Logger::INFO, "NXST loading completed!"); - return std::string(out); + return 0; } -std::string util::getExtensionFromString(const std::string& get) +std::u16string StringUtils::UTF8toUTF16(const char* src) { - size_t ext = get.find_last_of('.'); - if(ext != get.npos) - return get.substr(ext + 1, get.npos); - else - return ""; + char16_t tmp[256] = {0}; + utf8_to_utf16((uint16_t*)tmp, (uint8_t*)src, 256); + return std::u16string(tmp); } -std::string util::getFilenameFromPath(const std::string& get) +// https://stackoverflow.com/questions/14094621/change-all-accented-letters-to-normal-letters-in-c +std::string StringUtils::removeAccents(std::string str) { - size_t nameStart = get.find_last_of('/'); - if(nameStart != get.npos) - return get.substr(nameStart + 1, get.npos); - else - return ""; -} + std::u16string src = UTF8toUTF16(str.c_str()); + const std::u16string illegal = UTF8toUTF16("ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüūýþÿ"); + const std::u16string fixed = UTF8toUTF16("AAAAAAECEEEEIIIIDNOOOOOx0UUUUYPsaaaaaaeceeeeiiiiOnooooo/0uuuuuypy"); -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, " "); + for (size_t i = 0, sz = src.length(); i < sz; i++) { + size_t index = illegal.find(src[i]); + if (index != std::string::npos) { + src[i] = fixed[index]; + } } - return ret; + + return UTF16toUTF8(src); } -void util::stripChar(char _c, std::string& _s) +std::string StringUtils::removeNotAscii(std::string str) { - size_t pos = 0; - while((pos = _s.find(_c)) != _s.npos) - _s.erase(pos, 1); + for (size_t i = 0, sz = str.length(); i < sz; i++) { + if (!isascii(str[i])) { + str[i] = ' '; + } + } + return str; } -void util::replaceButtonsInString(std::string& rep) +HidsysNotificationLedPattern blinkLedPattern(u8 times) { - 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"); + HidsysNotificationLedPattern pattern; + memset(&pattern, 0, sizeof(pattern)); + + pattern.baseMiniCycleDuration = 0x1; // 12.5ms. + pattern.totalMiniCycles = 0x2; // 2 mini cycles. + pattern.totalFullCycles = times; // Repeat n times. + pattern.startIntensity = 0x0; // 0%. + + pattern.miniCycles[0].ledIntensity = 0xF; // 100%. + pattern.miniCycles[0].transitionSteps = 0xF; // 15 steps. Total 187.5ms. + pattern.miniCycles[0].finalStepDuration = 0x0; // Forced 12.5ms. + pattern.miniCycles[1].ledIntensity = 0x0; // 0%. + pattern.miniCycles[1].transitionSteps = 0xF; // 15 steps. Total 187.5ms. + pattern.miniCycles[1].finalStepDuration = 0x0; // Forced 12.5ms. + + return pattern; } -void util::sysBoost() +void blinkLed(u8 times) { - 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); + if (g_notificationLedAvailable) { + PadState pad; + padInitializeDefault(&pad); + s32 n; + HidsysUniquePadId uniquePadIds[2] = {0}; + HidsysNotificationLedPattern pattern = blinkLedPattern(times); + memset(uniquePadIds, 0, sizeof(uniquePadIds)); + Result res = hidsysGetUniquePadsFromNpad(padIsHandheld(&pad) ? HidNpadIdType_Handheld : HidNpadIdType_No1, uniquePadIds, 2, &n); + if (R_SUCCEEDED(res)) { + for (s32 i = 0; i < n; i++) { + hidsysSetNotificationLedPattern(&pattern, uniquePadIds[i]); + } + } + } }