From d2f369af9a22e56f3cae980e3bb0ed1c0738a7d8 Mon Sep 17 00:00:00 2001 From: Nikolai Fedorov Date: Sun, 25 Aug 2024 00:02:59 +0300 Subject: [PATCH] draft transfer implementation --- .DS_Store | Bin 8196 -> 0 bytes .../index/Main.cpp.685062F8D32EB8FC.idx | Bin 1604 -> 0 bytes .../MainApplication.cpp.79A72745C41D6043.idx | Bin 822 -> 0 bytes .../MainApplication.hpp.221A9A34681ED2E1.idx | Bin 648 -> 0 bytes .../index/Plutonium.176CA4CACA9A1290.idx | Bin 568 -> 0 bytes .../TitlesLayout.cpp.45180DB29A164B8B.idx | Bin 1430 -> 0 bytes .../TitlesLayout.hpp.75FC133FF2EF41D2.idx | Bin 624 -> 0 bytes .../UsersLayout.cpp.88C1C105F3F43467.idx | Bin 1332 -> 0 bytes .../UsersLayout.hpp.1712702588294560.idx | Bin 736 -> 0 bytes .../audio_Music.hpp.8B2BCABF9BE0D597.idx | Bin 1440 -> 0 bytes .../index/audio_Sfx.hpp.20BB10D088C7D73C.idx | Bin 614 -> 0 bytes .../clangd/index/const.h.BA34DD522C225A5C.idx | Bin 184 -> 0 bytes .../index/data.cpp.92E1D4C612A67155.idx | Bin 6758 -> 0 bytes .../clangd/index/data.h.FB9692DD59DD0CA9.idx | Bin 3524 -> 0 bytes .../clangd/index/dir.cpp.84282E8F15A0C3E7.idx | Bin 3678 -> 0 bytes .../clangd/index/dir.h.8134312F1E0EEB8F.idx | Bin 2302 -> 0 bytes .../index/elm_Button.hpp.018512E7528A66C7.idx | Bin 3668 -> 0 bytes .../elm_Element.hpp.14E03F7E0D260C63.idx | Bin 3406 -> 0 bytes .../index/elm_Image.hpp.FAD5FE2703E71E60.idx | Bin 2352 -> 0 bytes .../index/elm_Menu.hpp.52144C9486AEB8A4.idx | Bin 7952 -> 0 bytes .../elm_ProgressBar.hpp.5BE7F064B955686E.idx | Bin 3448 -> 0 bytes .../elm_Rectangle.hpp.E8436387021D3723.idx | Bin 2420 -> 0 bytes .../elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx | Bin 2184 -> 0 bytes .../index/elm_Toggle.hpp.F643B22B4EBE2394.idx | Bin 2932 -> 0 bytes .../extras_Toast.hpp.1CB990ED4FA33D5B.idx | Bin 1154 -> 0 bytes .../index/file.cpp.5237F76D2447B003.idx | Bin 5404 -> 0 bytes .../clangd/index/file.h.ECED60B58854FB92.idx | Bin 2402 -> 0 bytes .../clangd/index/fs.cpp.2C750D58CF93396A.idx | Bin 10148 -> 0 bytes .cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx | Bin 2726 -> 0 bytes .../index/fsfile.c.867FA10C150715C4.idx | Bin 3438 -> 0 bytes .../index/fsfile.h.35B524E0BEF369EF.idx | Bin 2786 -> 0 bytes .../index/fstype.h.DEBE4E158EC53C0F.idx | Bin 2320 -> 0 bytes .../clangd/index/ldn.cpp.393680BBDDA86121.idx | Bin 8148 -> 0 bytes .../clangd/index/ldn.h.1D557AF101A575C7.idx | Bin 4290 -> 0 bytes .../index/pu_Include.hpp.C523922F5EC060FC.idx | Bin 1072 -> 0 bytes .../render_Renderer.hpp.6683E79ACA4A54CC.idx | Bin 8114 -> 0 bytes .../render_SDL2.hpp.18A6D5991D9EFEE8.idx | Bin 934 -> 0 bytes .../sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx | Bin 9286 -> 0 bytes .../index/sdl2_Types.hpp.14E8D52331FEA768.idx | Bin 758 -> 0 bytes .../index/threads.cpp.3D4AF726B9B95CFB.idx | Bin 1834 -> 0 bytes .../index/threads.h.BB90B2C1D3A0674C.idx | Bin 1972 -> 0 bytes .../clangd/index/type.h.3039A978BD63D81E.idx | Bin 1226 -> 0 bytes .../ui_Application.hpp.C3BE54443DABA7BF.idx | Bin 3634 -> 0 bytes .../ui_Container.hpp.DBA564B6F57F0254.idx | Bin 1922 -> 0 bytes .../index/ui_Dialog.hpp.BA5E55C427BB5263.idx | Bin 4508 -> 0 bytes .../index/ui_Layout.hpp.30029BE965429247.idx | Bin 2100 -> 0 bytes .../index/ui_Overlay.hpp.177672D215C52C40.idx | Bin 1660 -> 0 bytes .../index/ui_Types.hpp.D33AC0C2311B451E.idx | Bin 3832 -> 0 bytes .../index/util.cpp.7798728B5E2F0E42.idx | Bin 5358 -> 0 bytes .../clangd/index/util.h.4B818D27750FB9A3.idx | Bin 5730 -> 0 bytes .../clangd/index/zip.cpp.BDB97FF96BC149B8.idx | Bin 4696 -> 0 bytes .../clangd/index/zip.h.74C41B03268B991E.idx | Bin 966 -> 0 bytes .gitignore | 36 ++ .idea/.gitignore | 8 - .idea/misc.xml | 18 - .idea/vcs.xml | 7 - .vscode/c_cpp_properties.json | 51 +- .vscode/settings.json | 53 +- Makefile | 5 +- NXST.lst | 2 - cleanup | 10 - compile_commands.json | 428 -------------- deps/asprintf/asprintf.c | 37 ++ deps/asprintf/asprintf.h | 10 + deps/asprintf/clib.json | 9 + include/.DS_Store | Bin 6148 -> 0 bytes include/TitlesLayout.hpp | 2 +- include/account.hpp | 74 +++ include/client.hpp | 7 + include/common.hpp | 65 +++ include/data.h | 91 --- include/directory.hpp | 58 ++ include/filesystem.hpp | 39 ++ include/fs.h | 54 -- include/fs/dir.h | 54 -- include/fs/file.h | 64 --- include/fs/fsfile.h | 100 ---- include/fs/fstype.h | 46 -- include/fs/ldn.h | 94 --- include/fs/zip.h | 18 - include/io.hpp | 55 ++ include/logger.hpp | 84 +++ include/main.hpp | 26 + include/threads.h | 44 -- include/title.hpp | 90 +++ include/type.h | 29 - include/util.h | 152 ----- include/util.hpp | 52 ++ server.cpp | 237 ++++++++ source/.DS_Store | Bin 6148 -> 0 bytes source/Main.cpp | 15 +- source/TitlesLayout.cpp | 69 ++- source/UsersLayout.cpp | 11 +- source/account.cpp | 95 +++ source/client.cpp | 258 +++++++++ source/common.cpp | 124 ++++ source/data.cpp | 352 ------------ source/directory.cpp | 77 +++ source/filesystem.cpp | 42 ++ source/fs.cpp | 519 ----------------- source/fs/dir.cpp | 266 --------- source/fs/file.cpp | 383 ------------ source/fs/fsfile.c | 150 ----- source/fs/zip.cpp | 251 -------- source/io.cpp | 269 +++++++++ source/ldn.cpp | 543 ------------------ source/threads.cpp | 69 --- source/title.cpp | 333 +++++++++++ source/util.cpp | 444 +++++--------- 109 files changed, 2314 insertions(+), 4165 deletions(-) delete mode 100644 .DS_Store delete mode 100644 .cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx delete mode 100644 .cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx delete mode 100644 .cache/clangd/index/MainApplication.hpp.221A9A34681ED2E1.idx delete mode 100644 .cache/clangd/index/Plutonium.176CA4CACA9A1290.idx delete mode 100644 .cache/clangd/index/TitlesLayout.cpp.45180DB29A164B8B.idx delete mode 100644 .cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx delete mode 100644 .cache/clangd/index/UsersLayout.cpp.88C1C105F3F43467.idx delete mode 100644 .cache/clangd/index/UsersLayout.hpp.1712702588294560.idx delete mode 100644 .cache/clangd/index/audio_Music.hpp.8B2BCABF9BE0D597.idx delete mode 100644 .cache/clangd/index/audio_Sfx.hpp.20BB10D088C7D73C.idx delete mode 100644 .cache/clangd/index/const.h.BA34DD522C225A5C.idx delete mode 100644 .cache/clangd/index/data.cpp.92E1D4C612A67155.idx delete mode 100644 .cache/clangd/index/data.h.FB9692DD59DD0CA9.idx delete mode 100644 .cache/clangd/index/dir.cpp.84282E8F15A0C3E7.idx delete mode 100644 .cache/clangd/index/dir.h.8134312F1E0EEB8F.idx delete mode 100644 .cache/clangd/index/elm_Button.hpp.018512E7528A66C7.idx delete mode 100644 .cache/clangd/index/elm_Element.hpp.14E03F7E0D260C63.idx delete mode 100644 .cache/clangd/index/elm_Image.hpp.FAD5FE2703E71E60.idx delete mode 100644 .cache/clangd/index/elm_Menu.hpp.52144C9486AEB8A4.idx delete mode 100644 .cache/clangd/index/elm_ProgressBar.hpp.5BE7F064B955686E.idx delete mode 100644 .cache/clangd/index/elm_Rectangle.hpp.E8436387021D3723.idx delete mode 100644 .cache/clangd/index/elm_TextBlock.hpp.B8CCDCBE3879D2C2.idx delete mode 100644 .cache/clangd/index/elm_Toggle.hpp.F643B22B4EBE2394.idx delete mode 100644 .cache/clangd/index/extras_Toast.hpp.1CB990ED4FA33D5B.idx delete mode 100644 .cache/clangd/index/file.cpp.5237F76D2447B003.idx delete mode 100644 .cache/clangd/index/file.h.ECED60B58854FB92.idx delete mode 100644 .cache/clangd/index/fs.cpp.2C750D58CF93396A.idx delete mode 100644 .cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx delete mode 100644 .cache/clangd/index/fsfile.c.867FA10C150715C4.idx delete mode 100644 .cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx delete mode 100644 .cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx delete mode 100644 .cache/clangd/index/ldn.cpp.393680BBDDA86121.idx delete mode 100644 .cache/clangd/index/ldn.h.1D557AF101A575C7.idx delete mode 100644 .cache/clangd/index/pu_Include.hpp.C523922F5EC060FC.idx delete mode 100644 .cache/clangd/index/render_Renderer.hpp.6683E79ACA4A54CC.idx delete mode 100644 .cache/clangd/index/render_SDL2.hpp.18A6D5991D9EFEE8.idx delete mode 100644 .cache/clangd/index/sdl2_CustomTtf.h.DA6EB9766D3A2A8C.idx delete mode 100644 .cache/clangd/index/sdl2_Types.hpp.14E8D52331FEA768.idx delete mode 100644 .cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx delete mode 100644 .cache/clangd/index/threads.h.BB90B2C1D3A0674C.idx delete mode 100644 .cache/clangd/index/type.h.3039A978BD63D81E.idx delete mode 100644 .cache/clangd/index/ui_Application.hpp.C3BE54443DABA7BF.idx delete mode 100644 .cache/clangd/index/ui_Container.hpp.DBA564B6F57F0254.idx delete mode 100644 .cache/clangd/index/ui_Dialog.hpp.BA5E55C427BB5263.idx delete mode 100644 .cache/clangd/index/ui_Layout.hpp.30029BE965429247.idx delete mode 100644 .cache/clangd/index/ui_Overlay.hpp.177672D215C52C40.idx delete mode 100644 .cache/clangd/index/ui_Types.hpp.D33AC0C2311B451E.idx delete mode 100644 .cache/clangd/index/util.cpp.7798728B5E2F0E42.idx delete mode 100644 .cache/clangd/index/util.h.4B818D27750FB9A3.idx delete mode 100644 .cache/clangd/index/zip.cpp.BDB97FF96BC149B8.idx delete mode 100644 .cache/clangd/index/zip.h.74C41B03268B991E.idx create mode 100644 .gitignore delete mode 100644 .idea/.gitignore delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml delete mode 100644 NXST.lst delete mode 100755 cleanup delete mode 100644 compile_commands.json create mode 100644 deps/asprintf/asprintf.c create mode 100644 deps/asprintf/asprintf.h create mode 100644 deps/asprintf/clib.json delete mode 100644 include/.DS_Store create mode 100644 include/account.hpp create mode 100644 include/client.hpp create mode 100644 include/common.hpp delete mode 100644 include/data.h create mode 100644 include/directory.hpp create mode 100644 include/filesystem.hpp delete mode 100644 include/fs.h delete mode 100644 include/fs/dir.h delete mode 100644 include/fs/file.h delete mode 100644 include/fs/fsfile.h delete mode 100644 include/fs/fstype.h delete mode 100644 include/fs/ldn.h delete mode 100644 include/fs/zip.h create mode 100644 include/io.hpp create mode 100644 include/logger.hpp create mode 100644 include/main.hpp delete mode 100644 include/threads.h create mode 100644 include/title.hpp delete mode 100644 include/type.h delete mode 100644 include/util.h create mode 100644 include/util.hpp create mode 100644 server.cpp delete mode 100644 source/.DS_Store create mode 100644 source/account.cpp create mode 100644 source/client.cpp create mode 100644 source/common.cpp delete mode 100644 source/data.cpp create mode 100644 source/directory.cpp create mode 100644 source/filesystem.cpp delete mode 100644 source/fs.cpp delete mode 100644 source/fs/dir.cpp delete mode 100644 source/fs/file.cpp delete mode 100644 source/fs/fsfile.c delete mode 100644 source/fs/zip.cpp create mode 100644 source/io.cpp delete mode 100644 source/ldn.cpp delete mode 100644 source/threads.cpp create mode 100644 source/title.cpp diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index b8a22bf3c9622030cc3d3cbc592be654b678751a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM&1(}u6n~SX&6ZkP@Pncr7Svm{t(8LY64QtW52EpdQl-symuBf^CuEbRSSq0h zQ4sMW2!eltz4(FPMN#jfc-22Z4<38cHy>$db`$g@md?P;o6LK^dB6O!yTjW60LeGp zae!d}u&^^b)Q>}l#{8@&T1u`QMN1G5PzEnp94{`=N;MH}7K{Q$0i%FXz$jo8_#YI& zGn+N5%egPSW;6;I1$LzZ?D=40XErBoOIZHsz=4+lXfwFX8_ux~;22xloU|=rITZI4 z)dP`GB1;S+;OMtmINF@FEnx*5h=2o;nTf1W2+xiaKVF;mKlzX>RvVX=B*2lS43_FU!mLC zJ4a~wVV>pBV*X`_P=Cr;=o|)Sk`XG8r>_rhet*I>i0z|t=^mx?u^FEpaG-&T1mLP% z$jY0Gx>x1#=;!+JJ%jvOQ1B{;&Jk*Ua+BxJV(t*t2#`fWk0tiNk430FHpthRgRi6> z+CD0m?olcqAf5Ou7i(ca!$su54x=>k^ubY;f*%{R+a8sjcjbQjij_E(Rnn`nA*lzr zDc-FSUMCHxp+cV1jH>c(eBo3BKdO(7eIcoIrl&V+WvzbeMzKtr#d@jUi1MY{Ec*bu|?sq`mDaWp#!U{V(L&%LLk7?s zn!uzn8276(uTOvceD58<2(&#^vAe~nk^}jMX>=a3pP%_nGYafL1=51^FsuL1c7OlB z1HYM}Y!omG{H+2cQz#bl7~MXk<3Ltx+t}x^v&Q0D!g2@>yc|c%%W3b(1%3mWmR}73 diff --git a/.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx b/.cache/clangd/index/Main.cpp.685062F8D32EB8FC.idx deleted file mode 100644 index 0da004a1f6a79529f13b1fc5fdcdd233f9bdb607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcmZva3rtg27{_nzp)Kugdt0Eiw55d>+ERHd-~+bEz%dzu$apO%P!OevQpQ7ZXhg&j z<)tGSU`!nZwh2+0!axI(No77zgdjK+0)hdYU=$Ex2bR-h$xd>=-1GbY@AtUp?C|sT z-AutSZ;f9@Qe0{@z%YyhA6;t7-ZBz~Es!xRqr9;=D0x6i+?n_4ElZ2&t1FgNS3jO9 zwBHf@YclWHZQr(|&;M>o-c_!Q*r^JSdWS8(S-x}7>kP>WGrO0^q0~1_)YWBKaW-A@ z%~O7G{Gl!L&zr)sK!%93F59o9WAOJ{X#~(`%tfEt)U5UdkMbkA33P?y>i3 zdm{hxW1m0I+wU6i|Ep|sIpy84_s#Lx)+WXCAVC^ zVm{VsADFMLt(1$!mBE9hfe|k@etS4{sK*`W)>iA9>h{0x&%SXWvbSK$NH<$?WNh~S zj{|dc9haXNO$N4;72dlKzbJF#Nj5B}3-2%b*}S^habux@D}S|gD5@rLyx`V4)3!m) z>48Rlm$++Gqo2=oN$uGd`gRi`jV{G>nMpBO@T8_*biQUk9i}re8w`^H2H>p*sH`q0 z^B(@lbV@jm$)PL6~ge`s9~F$=JSC`sW3{X$4<>t={8fCU|u zv`grcIh$4ZK+FbgL&>Gda~Gcuk9`3#128x!N!dM9;deFp+aOjdlp9e=dfdC8wS^l3 zAr=9VVH_lr=>K~gAYQo&=$JESceNCHvZ7!TXNof&eQYA=@7FXp$3v{5s07IEd7zS> zo93Acv4gdP9da)@Hqd8PY@GaLX6z&_utKMPo3(7 z*j46gi`+?R)G|ph(nwN|)>qpRSN7(U^yD%i}_XY1idPqQ~G*=1`c6ck+2fKIo zLN^mI4Gk_wJ&4G6mOO*lVXcEBawm9~WZp}Ncn&cg&<#ht*!!_Kx4-ix#45GwQ{=9& z(A?(sD8?b?1HR!8E~c-W4oPpDgIGfGkpMzYk(8nZG8SZtC;yRsEYgyc0ilOZ-`am>tl1cgeY8`F${3D<(nmk3Cf*gwhK BG6ett diff --git a/.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx b/.cache/clangd/index/MainApplication.cpp.79A72745C41D6043.idx deleted file mode 100644 index 3c7e1ab39e973d7f7786ff36184273ee0202431e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 822 zcmWIYbaT^VW?*nm@vO*AElFfyU|d!(yY4O zf-m$G^RA{9E<3%#kL(Ryx7SlgX8qItfPc1r-malnWNbb>DBWbe**dD9L4d((MkCXg zH7oPdH&603+A<|ev-#D%J#5GO8gi5qHr}g15HS4K7)*U4~y3ZXmybw3h{x`p-B;(qKx|j@I zXZt;@mcfowKOa`toBHJS316xGn-)ijnr>mQEZ1K!h1vC-qhN+`R?YLhre1Pt4>oJP z5A%Hgu`yO@x{_Xo!s&TTc{A3&T4MQ7?8=Fp;H}{=Lk`LuPHa8pb^X!X*wWy;937t5 z*Ph;d^X=Xr{LJM~_iYjHdA9r4nd{o_+aIp_VkaE+lJi*M?D~Ky-yazre{``nX{Bqj z&RN0uZLd??*Ok;xv|0G#$lMK|^)H9rwQtzyA=$I2WA4S)oAU16InO>j@3;`O$)0ge zQ~AN-%G@M7V0>}uGA*b-Q{pJVq@d2A#3{w8pvcF|C(OVBlmh~%Orv$>zLL9n8C2C6 zl-NbtWxxbPl7XQpHLZ9rkn_Rq(1d`qe$0$KO5B>S!=y?^~JX3Gl53(X!Gd7RP77!%n#gtaVy9s8E$o$FwAHs zF0lLV=~^6Ws=MFIqr}C*#Sc@IC&zKZ%&6=p$VgcpO_(sy7deR_9~BoR7wdxV-i4JmI{bZUS6=D^-{iXql!&b!^8cjhYGd~!y_SygoV^f2clTf-^_3sD&^ zkITuI9Nj%E4!x^+)1b0RwT1P5Oz}VYcPoshnccbi%Bsj{4dYC`f`rvtDcj3^wnhG# z8+fJdhpnaE@7KqL(x3b-5j!{C>i=80V>KN%48@hXNppcN`_EgGbm34c7dsOpCj&DF z7l$O6VBiGGF)%PPGb)JYp2_8Am*ZpN;9_7Fk`ht_6AT<+1q@ueObhDIlsF17F>x`l zb4qb4F!S;93B#nDx0G-NE-`*6#l*-3(ZI|t&8-HLwyO+i_Z8BCamnVXMW62>#lJ5^y`^7aHH6EhD7k0?yIu{-L|{{6RZG4g$ft01vHn+x>pf#SoJogyqu%mSi9^*MJ)^Yron8-_+?vqxEbN4*iKYvcZES^c*_*q}8-2Hx+|GsQe_@#xn zzs`8H+e1-v*E}DI=RYqWVo8wqV&&}K)=q)*{%>slKM%ZkN^f`l#*dyK!XvXh zH@%tneEw>OdXL!)SI_l7a%`H{p@P!0NpdGL89Xms*?eNs$@RxI`f5|I+OIU|GGQpL z%uU(_jN1zzE=AtbIT$Ozq{+yj#>L4c2qu6sU~uyAY?Cv76X)q`?-{w` zJ50AL1BI0|lyqQ*+*YfNIk+Pt5GZUeWG)YL5iHOcnNR}j!_DtK%?EqRfU1>6lqFz> z%&l7XD`RF=Co|A&J0)kBl|WvQQaFOArl6(@|uqsAr0r37;wkY~VU0pne5dU{vw%B9&ry@sNOQZT)+ z2xjEM8nHlOP^`k-wLE<1s;$QlE(Cf-UtV7cW(bg{sjq{?Gt@VM@n8YZ%n6SBd%6~f zn(FTN0$sslC=64?qh=vyAq(SmIx$S}%Y1hZ=u{m(9R*l8!f2qIKxrjUj^l)xQQ1ux z4fMStr=loK70^wb`l2u+ul=wrl33vV3}~c*uz@N}m}||4vo^E-frR-v_yu5LrT*z_ z*^zsiZ-Bx|dP;^cBVoFL4gjS_n8gf5sX2+D5GpQ8E(RxVcnW7=R{P@}^I{Tvz)~h< zRR(zcGB70PrsOCB^~lQ0urTxT@yLnt3yDdH3$k-?a&vLAv5E*w3rH$TG08Ik0Blq7 A+W-In diff --git a/.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx b/.cache/clangd/index/TitlesLayout.hpp.75FC133FF2EF41D2.idx deleted file mode 100644 index 0416f1dc87f34542850a2441d8b82f30677cdf50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 624 zcmWIYbaTsKVqkDi@vO*AElFfyU|)?o7jsF%d=Ocnci?VI zU|OWYBc|=wlNlZvM!Hz6emA*zw@jRSaiUfu7V_X_-%9ZI?Hoc_DrZ^!eP>{s9Z zir$%8^L_?S4j7??TaIW)lp0|!tJ2&5FZ zbMA`&cujzbkqaWi%)`qg4wKH4<2YevRCbe{iHVbenU#xG3`{U^LeyGlDDTeVTK8Fi zi5;e!kC#sbrn@LLt@t%irNw7&>l?4cG8uW8Sv6SoV1~}+WVdQ$i0uXn^I7sa!wg$% zy>#`(w&gP!nV1DQ1Vmw~mxu3Mwe|SHh0H+JqI?oC)j*y+pAr&JolgtKgZZ723+!i@ zNiaV%a)SK|6Nb@1)gX`F)3rF%RCm9(u{-L|{{6RZ0bL1l4#eX?KS8|?Gz8>jm}Z8e x)SN_6YJmBf0WMQqlw7P2lwo2{ZJfD(#YqDzW;Pa922k>W0yY?ffq@ml1OSiPv~B4Aww_iaA>+-OiI%6lklI&z`m< zYu2etaUo^=+E>>u-I4W9hiCKd6*C+Sa?JDNbF^P=n>6={k~mNM|7J@ACI^NY8SFh9 z)SFAqXHOLl+vvP*YS*s%?_xHUEG26jo|aXA-+WVNU(R+Wsh`S;K9|CErMgT!nw$TW z)o-7;J7?}rkBIfLW;7>&ypU6%H&t>u*kKZ#lm6$jvGy8Pm3tc1F>Ra~u9HT(Gt4 z_e0wiGy8V+v>yl&;VWGv*bl*2HQ*vuzqS?SsNT18qOp%d)Mbm zv)%hd*FPu-fA;@2H{aRvZ?n&9FVC|+8Gn^Ib=?`(Um+*!3uK#q^z1odIITu1zd%H| zcGo(OE!Q5#oLc_7EOgV{DeGQ!&rXk@_p9cj>*9#eznA~aw0h(3$o;98U-f&=^UsTC zHava8uWwk*#ZX+Cn^Xggw>(|FxS5N^IK`M0G#OOc1=(ekc{q54890D)K%leI=T_v5 zk9q=3>Og5pF-civK3+Z%m~{4!`oik@;raqh@<3@(Nl^`D0RaJdn6y)-(YkV9$=$pR zY8nvpR9R(Ml_5+9hN9H8;wC198yLC34%ipqnIE|Q;@1DXHAxo^rE)RysIqdiO2G^> zc6Gm&e)AR&P*_k=&=6+Wdq%GK4%6+*Kw%Db4sDpfU>*RP4RSrqY?#A=!XQ_}gqe$~ ztt}5fvj-X?$|A}QQ)Thl+xo^Uu}mf&Rdz*ob(q;eo+i6Kj5oJx*{_V5Rh=wA2iS5r z!Q2Jp1#*NTcwE|C1_&OXJf8}THAN-H$)~dInG!*-clQp59fva%nbDSXxwC3Z{B__|8>Zj~`sf3^ZF#QV}K$F*jbS`km;n95&cecOps4d0p^}-KP-zR7I;4c z3QGw~slrv)6wY_!W_bk^Ruoc{g9)pD`dW76p5_~%Fo!OO0nCu1)SN_6G!z#l7lV_h z5-d^}m{pxNPnEI1`P7w3MTr3(Squ!xxhXk{KxH!W(#%Xeyxg)Pe1f9lVghXJ99*0n QtSrJpQv4E%l8kZ;0A5_b0{{R3 diff --git a/.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx b/.cache/clangd/index/UsersLayout.hpp.1712702588294560.idx deleted file mode 100644 index 66a84d75186065da61697f49be7d25b7a945e4c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 736 zcmWIYbaT7G#K7R3;#rZKT9U}Zz`!5~#Kk2=ne9OOI}lgQ>Fqnnb;v-#<$L$8?=iL> z3%eFHUEg-|k<%Ss&uuU6&;GVxuO<)k=LZK5%sX{1NJ(>TMb#nG;8U^xgR~6_71}$r zoio)w%_?#8HRCL>M_x^9K3od!BS>KyI zCpx=0NAa}P1c^P2JLJQEGZa_mChY>cB~Mo`ZsuY!PBA7XP9PTun0YvOgc&#>9G#Uu zw<2eJ)DvK0gGux8@`=EtvwzeVR?iRD7hqz9Nec)F$it+$wUzIi-zbmdW8&guVCInL z&;S!K>3g~shnni{_cAfDLu_MUX69n%VSpO7FTgWDaQnrr>`Y7?49u)ttfF87CS8=8 zR;bmc?c*B@(4^A<`JMU$OAB8=AvqA z%frv?8F`omI0X1%s$l+N?~FyXnJ>{g8ovE4vnK1)7l zmp)1cinbKvF-IJ2OJ;IhGnahqU5mu*NkCT{y~>tQx&)BEJT-+AwO z-&?pjJG(~$U@zTVTUA!GClCPS?CY%IE2=~QYXbmkd)-I#9AB-Who5ZWlgjk{d%Y>A z4L7rQziPjDzUW5lm9~s-m)GN-zk16xytQ+lpp5TQOV;Q+Qv)et(j>7Ve5^LQ)=lY!E`~!3MV%^Li*Y^D!P~JF}b|s&f!8Txx%JKYp^bEfE4@YMa^?#d4QG%Q`cv~-#5Omio{;BnlrAG`R;9ZOwl{^zE3 z@o~+ebv*q3@QmeLydwWCSHV12HIq5qV$m#Qc7|=aJ3hItGC1}Po9$z1L32=~e!aIN zJ@?xaJ-W_;w%*czyyfzg`^x!}?cXeaG4fHPb%AcjaF zAt8Q2+$HNgO*bY|h(>{iXr2+Iyf`;B!p|jB(4jy}v}z!-ApZ%D_QA*hPA=QUVnfnX z4|17Y@zf-A?HF1!F0$>Spuqd1NEExDIf#j67HeL<-f&j@N)3f56sU<>2P9PNAE0*c zKD2g1+(bc*0u@n30Fegx1s&d?5B_p)=%b)VK@5pu0({6_J!8_MhOdVyC{dscRWggK zf+5n#JX~%_%Xo8Z>QxF_6c~vy8VC*ak7zyT;r4e=j8cd|fi6U+@{LF|u@kPJ{&HmN zcdsu}P@y22M6(g-;kF@suyB)gnL;=UOvJ>ZA|n3?;LG+p?>&>cB-EES?w4}PKqXmu z6i%g3MXjucQ|r~SE1Tp@Dw7rmVn;;8*9|-lo58Tq2*lV2hJ_v=#uhLvq>mWGWmpIm zF&bf5h!QcnWmpIhF~-NR5F6s}74dl|UDbUe$|)igT6`Cx2agmP+&Iu%E; ztBvZH{=d>1v?er7IbEPGcqKwkuhYliR&bwexKIfkj)h=YC<0((^-cN1geem zdHRzT!3%b>OcE$y-Ya>1(nTTH-7g;QnDVcB?GE1D^|o)`oY`Q!reelk{nV?xM{P?u`!O-InGTQ+`{2bXEWMJWtWg%EdqbaqEr1#h>hMJzMs9%csI^>#kMH zyi%Whr_!YA;=-#!41SKk8P>hoqU3L@arf}UrB!U*>kGDMPC8t|ShCjFi&;HmlGCgW za<9D)|LHgL`&t&XzpEpeZ@%Fs^@LA_IUEA(B%+JYhtIyN2Q;B5HLZ9qkOOlIBPZAm*}ZFi|G)aj8Ri0@Fvw<@@Mkrt zTTfSg+Qi7iD=Z)^39|*}9H45DGhnJ=(kxtH%VEMiyrL|k>@XX6c*O+8L|`WH@QRCw ti^CknP?VaJ2y$_8QF5^|kY?gt`}C$L+s5jXO#FNd{2(a^;Da$37yu=U)HwhE diff --git a/.cache/clangd/index/const.h.BA34DD522C225A5C.idx b/.cache/clangd/index/const.h.BA34DD522C225A5C.idx deleted file mode 100644 index 436dc3f0752bdfd7520cac7e84554166a1e2e65a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmWIYbaUIlz`)>~;#rZKT9U{DWD5duaY<389gsEx;)*%R9;eTEZoPEXS~m8`g&gVKX+bR@8anM28K0tg385}xk*3+7>ZKU ziuHiBv8(&F^qaSM7`Yf3I2a^AQXnw5YT2)hnN^)YVMY!{UWgz_S56{`U0jr0%nhWO Q82hg?B{bBo+Q`5F0CA)?)c^nh diff --git a/.cache/clangd/index/data.cpp.92E1D4C612A67155.idx b/.cache/clangd/index/data.cpp.92E1D4C612A67155.idx deleted file mode 100644 index f4b842120f5885d2b3669a65a3a30759d8808ea2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6758 zcmY)z3tUX;_kLef&ErnZG*eAVHKm6~@1pUjXwu+bVl-0i9$3G`Z%8-mK}d>bV{N0dmF_=t4rZU zmM@GHKaV@q(^EW2Z=LA<+vXb$^CMdGw=V0xU%IHd{o0cYTlyDyJC2w1FBz5O(9~by z`(|ZG;hgcNS))+-<$$|o?VaroG#OiZt-(F8Won&nQCTI z@r*aWuPAy2@BV`&55xC~r)){Nac^_K{|4~@b+zOva-Dnk&@=h6_a6Os+IaCRzF%N8 z{^0S5x(UfgJFwL>kBnSy+CX2SJoREmw7ARS>-_mTdvAE(a*5T7@l3y7btJ9Pp|(@7 z=K1HIy;~EOJhN|_J;(05-PwQ5SS6jgllM!t#51XLmG_}1o5OC>FaC7B;22teLa=u8 zxm(M%(tg&uaq{fzpTGY4%O2Em;5qLpUOKp zGWJQso@jC3dhgQ3EtZdCR;JuVH#b&gzbXFlVPL}El4%WVf@F1HZF1E9zSMQk`K1lj z8>NZb?`HHKKbrfzp?}O`LDBPPQ@Y#kpYop_lV*ECr@MO_<8h?KV66H7;_TAC!s7K& z@<}iM%qculaqQ^8ZBg2NT;*~4@HXDv{a;v_#I1Q3;OkslRMaZP50e5;1$mAfzh3pS=;)tIJ_<)wzjaW-}g|*iPGYm1=D{}Zu(tUA`eZU?jm&E^ZS~D2MwO} zC+piUw^voatDTsZFgAbx&baG|TX&%wk_O{_`%?Gai@%S?GOw~9c+|daG5T_| z!gt!!)2jlNPQuNtztx?7S^cH|6GiCRSw)K;JjyxM`rgPZ>-*i=-c{Yfr>v8c|7dWY zFz_fQ!cX!3QpSR9G3Hm7on6s%QW!NiW$eM7x99#=e`RsU&8UaJ&a;ThD<6O4;S1S% z-Sxw)Jz5W}z4iLfzTNIWOg|HM@z0(6e_Ni{XKWnsnGZg**86$^|AkDNw!ZG+EuFqY zDH~tEdGf%mv*5M6Rcrb#mtQ`6P93OxQMKHyE;Mz&;p9Cp|4GWZKCRH*#ynl?+<@(8 z@4JHvH~eS4CD*O+jbQFLp+)-2T0$z8q8MY^*hIaMtE_(ReIYG4wjx_wNX;Q=eItDf^#bU`b8nN) zyn6L$Y5@`O2nik`6Q!6ZHdYJxWC9WJv}sbwuT6I?Xv&pv@RWFtR4>3G+0)RxWTM20 zrhEy9mq_pu=_~bl`a+h2|H|&FrIxntLMl$BqP4MudI1hfL0L`2D6TI}O(7h*u%HV| zl~SJ6K*eFSS_h8Y!7?}DexQ(=OE`!*Vu5-A4($V?ySsjPOwjWQ+5tPz>IKl(ZCB50 zteqW3Q}c*`by%3GujPawB|s~N zrosu@S>SA}UI5*;BdMT%$+P)Jc##jqRARakilwWvr1F>{Ce(J8#gYfEWr8X?(;O*dj!Wr#_Lv)Yc6JqgKqNoH}$ zo(DT<%8h6dAPJDGmq8H}6?)Hgw3*(EeKZw8I6OmwXT%eFJUz819Ms1|$=}B8J4Xrg#+Ptk$GpT9CJy=$~UWHH#4BB0(-1rWEl+ zGK$AVl*f3&{8;-EP0b+aRrD&kdI5B(=RcQcM7MT{aI_D_v|w=yHYQoj zNdiIB@VaHV)!6RaG!>*eKi1F1R>~k@kR@xwmebC~TRY17MA)71Xv5+*ELV;fjvo#@ z)~!n2?I+;;O;a(1$6F+Li;R^4yZ}RrM{7H&VT6v3k<1z_dyK+M5i*pGn4hrZCu|K@k7mwe$$4xIR)xGrlvX7@ zFgnfbLjJqZbddFlc4b}QvQQmun(=h@^kTXDnE8wvN(xK?;uIhQ*hCXcPin(KU?7#j zoG)Q7fWs2z_sNZ(aET?$kZBomgN=}FG0psq_ ziyVQlVgY^WD{+Vl(Shj>tPe70M{Eq^t+6*Ch+T-@g`{v|_esCwupg7&O!93K)_6fM$22WPqWHYx6&<-Zb+Lao!=pP_AOKu|YOA zfxJUAd03E#4Tka|vk4n)!X{8Lq52cWRHYxzx367$;P4EO5ORzfEUm#d;7R>!+1IxS z^uh?@B<7yP7Jz_0fiN0u65xb_b0;o8;eV#u(T(i7(Ihw#WC#+Vs-6Kvs{+)u5={WI z{N)kZ{aKk&WPchbO&2ZoNpy2$b2P#;YFgA#K+?>-(es=jJZWaZB&X3BI#+6tOXC`5Y&0DZNTsh!DqFL4E zuqWz3(hg(^C!(3lNP1Zl?nOquY%~LVV#>jG=-;c)H+OGDWKydMHzAWIO}H7EG;6{g$hLzOOEaB_{0n0FUq-ffHD|RkTqmQYcj^m0Ta$_NyAa)lh66p#Tt^PqkrxZM;g~kO6f!-Lj~zR) zH^6Iy7d`qXr#XdOziFIlhHRvej?4t0p8LBm%~`T0l_-&qIQeYl0l)BQ<`&}LLVB#& zKBU*DnZL5IP8Q~~w{kBYwHG@MHG{;_I+dEa{bR?g0!`3@^&!wyMO4tn7BE#a!SBX; zkY*}nPfbpY$yq}*-iCFyVSQ*!^I!aWFEZ?T4ms}$%sqkiA=Xr|-E29 z<`E7t_A!oN3fE89ueU2L$|tW~I4)cWc+bQ3TtD4{Lh?G;FxVNW1rM5Ure^tUqG_fW z$%~N-Yo8J%FVTdzBl&hsxD?4tHQ|HE<{OGDWK*jNHz1b=G?}GuLN-mB z_-16&tO>UwmsU;sUNo{7DPX_jnbE`c_dVP~>Pvuqz|i?- zW~gE4NH_q^%;wG>%I_^dPe}UqlMxk!Z4VOlAO_fi9SPgPp$gc-G(`}DqZA;9uWn1Z zz2o8$B5ykuY{&X+zOKPBHJHrXgAWW_0=|0Eg8jrX22nCr&3Oj%G@uy07%7WeAXzY! zPG~09AXbAiK`%jrGEpy416ruJPy;H)++wT;2Bev0%xPwm23UfG#^AiiDmsAp1IPrN z2<40rs-`|TCe3U?+%0T&g8`6KGclR|Sh7OQDZ~OO_%!J#Y&PLB5SfGx7Hq)=VE05* zFX{6}*(qY?ax|+PrGTEKfklSL*!FxMuXshy`_ZR}Yu6#pI;0DP50by}@7dbbs17B= zU2L;;p-5i0^M&A7f{5~nQh>Q(G)4HMk<_bq(WBtSCg*fW$rCC}*^*a|49hip%W^ce z97TY1nyEr2RcynoYGVw$HR-#NUpI;b`k~2}uO53@C%9U>YIGTH25!cXK51r|!7^iT z3(agsdYh317^;#dw-^aoxCC)aG~w-tyIm75Mch(NxB%lP}imv$XQ6}!3bvNnXLOnuykB|e`KzA0ye4eFL#E$- z+$)Ckpn1S}XNhjvh)<`f2-l(!YmqCkR@J-_SuDJFyZo=%Cz0<7VWdywP-&%^c-Q!e z5D#gIpOSCFac71sK w;1`ODPfqw$72Ng~cKUiU6XTJV!-mVPt;`H0QX@mDSY%;tYdT`0jc^q8e=duOivR!s diff --git a/.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx b/.cache/clangd/index/data.h.FB9692DD59DD0CA9.idx deleted file mode 100644 index f72c8d30dc07e647c8ae6e3ab56434a3d155bdc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3524 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 diff --git a/.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx b/.cache/clangd/index/dir.h.8134312F1E0EEB8F.idx deleted file mode 100644 index 7daac4ae8a98ea8869c8c66669a8e4813f760764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2302 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 D24zpX 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 ccf5285a153d1ddbcdb896247dab9b3db0c3e911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3448 zcmY*b3sh9s6@BjlBI6a_FwC1b!`%72ad7yih{&%8Q1`B&FUvcXh^iNelWyOOd4xjj5LxOi!Ni(P-7%3)mmGdV7sEV=e_nYl(pDvy}jQ( z@1Aq+*>_6Z`T1+7bKJa@ZePQymYP70 ze|AgB!87X)ADE}Bcze?qSGKr&JXtroYkGcgzeR7_7d)#d()-Q+9R+u*zS*-WCH=&^ zSH|uHXxe^Qw(ZOFNqNJ6^Gv(_l=)-B+q+3&-*JuYNdL`k&X4S} z=gxzdzkOsp`OmJ47sT}Sals#4e=cLD@8@0f^S36-Xa3cYSmPPHIn(_4v|sa07yr1_ zyRl(;78Iub;vvt-hNJ+2OqDsd+O*oG0yh=v1%C?yKF`{|>!W~BVg;G$m`Mec|N6p{ zPYxA5Lj|G-d4;qhN(GdcC-e^Ps@_laBmiW+L!YJs%6+NlKMj21h9Hm-kV{RaP8Co- zoPM^i-By(lN^Bro!YvjRP|i&ad(?NVC@GkP+c_4l3D>K@g{T|n8UIpD`usy_uC!1R z1?!E1QL6&3k2@GMTHLZ|oRH}@HD_tHKr|rd8S|o5K>4$Cbze+-U*i>s0J2Z(lT<*t(SCc` zg^vRsrSap@k<{Ze_bbD9BEOGg1YVD}_UC z#pTa-3M3fhOhcwk1(YjBn@e{aPJ$c;vRCptR6yCe{rPPNu70#NlsG^(>&&7GDA&$E zYIRMm-7XNVtoDIpt=d*2%5Mz6-eN58-4RNhuv!u%tqLeVeDCrft~aLc5=aEd*;2M$ z1(b8%Yafex{K0`x5(%<3+$yPna&KzFywSVAdrKfeAlFOvGgLr1Zr_Q0Z`A+vWGI;d zvPm#$RY1AWRQbZ1;hvKMF@c;YC7M-0c{pipK5HBr5Qq`vXervP0?IRv9gE%ByXlNT zLP0K<%A-|4nO3aH_|p;lpg?pW7fZ!4Dxh4L)zv@#3+p+7h*-5UZJ8P6%g4C!^|tz< zP-21AkwTPJHYkw8w9(rhZA{AG9Y-kqmc6MyG70xRRlpP*%t4W}q`s=5cu*!{$=f64R21hZI-9R~7TY@aQ{4qxucf z@YPXX{(9o8=s&PpF&})bG3g%>HA@PtQz?sJ5i%M9WqPKUF*IcsYSE%aQD&u9EuK%x z@^pC;S^#D4$K12A3zQYi(dWJ1sjBb&Y{>ThV;#D`(!o`bc5W2O+ded&nsq$Z^KLN@^knS zWeF_7Qu)6_L)k2GmH|fx9Tn=S*eM3U0>YjY8$}x8{)U$?O{DdH=BX>WftQL2W#QKF ziC+Hy9?^E;6N=|zEG!1liq|5IHVd@ku}EX}fmXZ~X|z?K6;DMP?G$LmOOZw!1zPb? zr14||t#~KWXqzwxif1AX$-TnMdnRf?*uIgCaB+LP8vEkRc zLxR&gxo2L4j`}KmwU{@$F(0D3Fn$$X7#giuFw(FYpS;oE=qMIt!Q?565n~K!g6Pwb zq+-$-;FssCe-i)Izh5PREFwK31N$Dx^xxHI;^3SgIJ#`kz^XkwVGimr;@UTtynEpI z;BR{aAOovU#th(Twl8JU(-Xc0_=MvIX()FAMuIdx;pNXJ)pQ|S=)j&+Rw9;|apH`7 zU1gaMpFa{#nM9>9EDU8vY7E19pv*)~VR(H|7D*$+(30U}7TNBvnH(!8OnOR{GbTN` z5%I?nY!v|NT|Oap>6awsI%q+t_xXmAe>eJU2j3cJk&`o|;;SI9Ao|EO}>_gU8tj z$}7m;v9Bth$0LUB%3(y0E7O;s8uu)Z$y9!NXK_zz={S$QEcd>)qVCb(AMm&*Z}SST XLD|08wICu1;Vpc$BmkAzfZ zQBWbyN2b%LgUy*a7lRKn3F-zuw#5aFPBbpdaGQor36o$ZamT~`z0I2RZ_fYa{O3FW z|9z(czt2~t?438muhp{+l3URokP zIq908=iOTLDY@L&v_U*1so8Rc(HgeV52?8 zlDjx|SHwRb**jV;T9gBWYfASzH=J*%&>uXNKYaYCeOu$z*L!dEjYd7`A8KtHOgle# zE%M~1qdm?E^D|5Dof&MGrLz}f|8AaHUsSuc`kgNk$4weZ8eQIYc$|Qc$PvHApTGha zMej*J`{rgPu4rBpDWDh%7s`b;767i8Gi~Pbt^F|q3Zrm}loHPZ!1sE)@}BqB>-ES& zVNno`EC9^?7V*n$>sf<L5rwIsCI8)9{U;*G?_XK&bOYjM39ECHa3>ymok1uj}CwFe} z>5-Ab7L&!y0>F;?YjyE=`2rE?D6C+`%mTojvF`c@tFjgeD4fDJ$u@xnfYXm`bd@ar zdWjxM6qW?Z$O6FU+XvRQwFJur6iwk=Id=*R05`_06yhD{+Vp5Vg=IlDvHr4aRUW?ciXO>yw^=Y7Spb;i zTyuSVuHiu>(wRA8iZ&4zIBFL9;R#>(W5rF+zpal1WT5-qvU?&6u)igF!{E1jB1Qxh zL1CxloWKIWX6f#Ol=pQ{1*E62Lv~DL0pOal@>*ZGrdd9EOt2~`&?-$cp{i-nC`~hg zs%g+>SoD}M)ifr`E?(Kq!-O$OQj)B|8>P-@H(YKfqz`^vNamWQWgHt|a1%UX-_8>$^yM{;;Xtk`OS@q>A28Ri_d3|xs4 zWRB4Yp5T>}FNF;PHRD;5)jT znNHmeextmY*)(4Jcx8VGuT&+~3Ua|DT}+q2P0&DEnHf?oaX=S2BgidjqKykI4wQo+ zK3~vr?9`BB@8~^dA$i=#^lo zz?E`jwpUA7TAd5MqaU6SF)?Ga0UVBrh(!aq022vI251x}GL{WM36nS+X8`rlo=;Tb zVNP_Fq_{&?uPW0+R{2Un$f{DQ)>bxR6JcJMIP8we;C@V;#3^c)bKBjM!E%_SkQ7my zk!Ol08Safq2FVb$-efv6UC1#CmZeai$59l9Owp}G<#yzG_;JS+0&|~;Y6BdPpX=(DXS_er^nu3b|5iuW~ZG8 zDTe3GnYE*BnU4qJ;J%mSs+`_BUxvGqhh{Im{nuLuGu|BLVKR$q50zFtAAH6Gr`np* ZT8W;9NblyBKlg4gyTzM06Wp5P{sX?o=C1$% 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 c35e70003a85390f6a5fc1348d4ed810116ab691..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2184 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 V%kFzeZZYw4GjPM~VPN1zFac>6YqbCX diff --git a/.cache/clangd/index/file.cpp.5237F76D2447B003.idx b/.cache/clangd/index/file.cpp.5237F76D2447B003.idx deleted file mode 100644 index a0e56d544b2bf32224b9474fca139d7620b073f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5404 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 diff --git a/.cache/clangd/index/file.h.ECED60B58854FB92.idx b/.cache/clangd/index/file.h.ECED60B58854FB92.idx deleted file mode 100644 index 18e999afb9bf0a34e6ca881a22e30dc607a76358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2402 zcmYk72~ZPP7{^~05H>8Z2}ihMIF!YR+>%f(MFc5W6cs3_MHH(XiUA~4s)$;xXf0Hk z0fj0aP!M!Rg$`B`wV;lba>+G91*}D+6%-KX*!qTt&`Ax1l&8+GS{L8#XwVkYw8SJ{OE9Gz?Oqn9b)?en zNki1ifuV@ty!|P|=N3HK-g}}@x#Lwt%igga-#u!&QIp#_l-(f9SGZY)pQkcHSIeJW z>3lhuE3Vj@J>9m>$^WZwpV|8O)}JoAFx@vY?35qi8BlO9O-)!ZGtP{D{Y@;&cSw9X zF`(gow9Bj{**k}9mW2%#^@#pcpIQ|>HaqnATuZ}^|N zzZKR#cTTWMy;T@6e9?7n*C~(qzthE*&ujizU+)@}Q7sxsSQ%cFQJ>Vh#bWffM&%1S zvoF`_vO{L-<*TQ4UX@BRD+*nLV}s)2<8)TlFO5Fj{Ik_cpZx1(%}QdsUXET$$bc|* zsIdIietm$@2zuB#juB!fP7i$DNCzcnO<{4=!4_H23Ggzf9-j?-x zB(tSRe5U_Py}W!gQGTF@S#^H(r9(DPa=SD1O!syg{4Q7;vj0(OmtXS_fr6;-Lw{M4 z_fB)Xe9zKa-eUd1nB?@tm|JQH8NKV_O(K6i5#ei(UkUs$48`amE!B_v3*Ro@wWZfk zm+-VljBK{diZkGLk6AIJJ&%4-6(m5j6xm@R23O7 z(o29yZ5~=|iUS_|1tqRlSOn69H|%dOus4AX=9D{VJftqfP)H2Z7`Z7tVJVxQjRg`4!_iK{x+;S4g4x zkZ(X57$Q=iROKI?5NEWudS0NA0|Z!VThfT62%|;Dc!G!FX(hSJjX1y+0>q@)_(KOR zhrBH68rw@Kl*~ znY5KAT%fqKu`>hiYw2HXv?mH$DP#rBiAWKiUq{`NYi{`o@AN`4q=zQVaCuvStr2G+ ze^a9C=l^_~I!qI8*ew-EMVtY5O4f)?Snu*l3Vj0QJxNbo9uCL3>hO+-+NURV2|3P} z8OzK$1Nn;B&B-<&am?d!`~sM+UU&H&>5&Em#aOCa^50)3Bd6p-Km#D>@j1957rxc_ z9Ml%sk8}E?I;ts#J$rCc45Pys!Y{y`M{<}$3P6r!7H)_JxUtMa4bgx!mRXcRhHi0} zEX7a+0LIZQxDXB4VwuGiq5)SdvzUSmm|~g56N&(wSZ1LF8IZ)CuslK$KoLi?2tqUf zh_668PwD{g1E0eXasfk}%i;&Qa9>zvp#vF^!!nB+$N(CaS-?OBys*q-1u{T|<&Q6P z#C0^kv-rn4+%D})2gC7Fj4)al2R8=9;S*qSgDHSCEVGz_420pGSh7F{vark&1$_Ze zxCjdqM8mmZnS}^s;0G6Bxq%`;4UT4!foMf+!UpzlC|R*FIe=YAS)+R9!2%)M4@;Z( aOM*#IT#J>oI33flmCm!7i{`#}q5lD<^7-`u diff --git a/.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx b/.cache/clangd/index/fs.cpp.2C750D58CF93396A.idx deleted file mode 100644 index fb70b75f9a7651766e36129d6a3e274329fa6d9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10148 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 Z{vDiMTy<`)PL4j_ojp57b+QZM{tx82-}V3i diff --git a/.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx b/.cache/clangd/index/fs.h.B7E897F33D9D7EE5.idx deleted file mode 100644 index d74e01762a71fb220285e6581c3b03e1356d6a8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2726 zcmYk72~d;Q7RPT8fe$X21Va`KAt4DMkbo=^3d$bgDN9jCtrS5~E2s!m;XRcO0$Q!? zwFETILj~NGip&UQaYX?u#b*_2eNYr1ZmqHuL+iUA%rSm5$=vzp_nmvrJ@mw9y(LDCArU)d1Ewsq^iubykgU)pC^{p z_{;?(}UfMRuoj<=Xsnzhj;Gnngra{XZT(hI{Ot7A>CJo7pr z6KXEcD?Quxms9;@5j{JxwoNcCB< zAyzk%R>%vup#IOkWquhk^(TCIe{MHxe)7))Vg#&0s7C+_fhGIx2n{d(0mfz|_C zL@RAxMnCChM5%jyd;1(tzhUp}y4w-lBsty_dA%dL`n%rE-GW6)3qx(+8PpASF9`jM zXIkj>d*;Qf2h{&+zn8G*VBGr|Q5ECkysaB|8;5fE>(@$dryf0(6%^du;ri3P2u?`Y z8U=O`IBD)CwcpQrsPoM4ZB)^sqFlY2|rzfMsT zm1=bC<*^AJ=key=V#f_f2lHL+9|ufa|Kak!>md2HU1^LvbHhj z#!$qyzxH@+G#$TM?W!;`Y<~U8xg*+}%rKeRs^TjxI?qa-Z<3_2wQzJ|xF}`ez_9#d~n)Rr777Z|CY7m#Ll?1tFZ@M`W@9r3ZJ#K#bt56@)N5H zK3|e3G#q4gsx+rOW{=g*x+3Z?UA8N{UYizjzM`#CY)}}~@M&U8niZCqMI9e7-!Rtx z%W#}cT@^Fo#Bji)gJ*w~-Riki?pJVZP_tVzIkW8Nel|+a+LXwffl&3{lPiX@w;+t1 z=99}y9;GYg%14y7ISQ39l#GFk&*d-H2pAZ710XY(StE*Vh=Oei(Dk=!dHHc0GmJg~ zfY0O$5M^P3{Al1y-M@d$dvTwI(Odu&l!Bw*Viu!|S2a+?#V8N}7s|y-4+xx0)@+C^ z;9=wpfE(o|)B`%JR{tVS#VU*x07xmR2vL?Zr>n?%8{bsqXYn~kz5sA2j`>uQQ)Lb< zZCd%bHf9?}K>!FSfhD4>Or|5m56@5ka*VYbBS!!vl!RQsDF)7001_W4JngjJbh3l!hs9K}ct zfFtF|qLZ961*9ddOOGNW9sI$drhH+$|9_h2M zDk6JO9xRZEC>y^S?W*ZN_g6!z$>mh z396sIDlx`Np-5=|;R-3m3bEHmrF%&hz>ZjH=VKQL@vu@FC=G|B#7dc=48uLaO1YsN zgSjMqtlaLit(A0YN$eCz3DW%S7To1~`}CKf{I zDO`FY(=!2R7zQK=Jp@383qoYt`XGbxiA)mQ9@*Tgn*op^M^e4 z@sf8$cBkB#Ag3jzB+y5ap0*}Eo?f`pU(=5lERcb6`B9eN;CN+$v6rX2o9jFe|3Xlu6951J diff --git a/.cache/clangd/index/fsfile.c.867FA10C150715C4.idx b/.cache/clangd/index/fsfile.c.867FA10C150715C4.idx deleted file mode 100644 index 4cf7d01337a32ae4380ce638d60eadc71cfe5916..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3438 zcmYjS2~-nV7OgKKse~dSfdBzn0tAArLQ0lE1cU&J$R;Q#14ERewwEt{E&axp-qv?@PHGk~rs3+%-x6WH` zp*o((q^qJvQ=a*nZBPy>fBSat`kAs32e*u%?`2Vo*Nl98`1{-Z!p!jncZf<$i-D-N)vSv9peR8jBu2BoTbx_EQVy=6EA)ez)js`0dGI&<4&zDwo5$*9gx<@n zY2s(*z~u-R)>t{2((d({;{5y4|LG<-En-*r81)Nx}JP{^#tM$JzkXAI-f1R~w;$Nz1`SJ6ma|aV- z2miU}WINNd?2m~&MY~h=(=mCm-*)Uk)XPA@{gJw`@1(9xuD5?W`lpwz1IgPITb`W~ z>>As%pVeh^@G_GdwqL0#w7ITxqCrE z-3NSP?5@h1z!pM+E6tUV@M(MkV;az~kol1CB=!FCrcQMrfdxSVgU&FMnA6S8FeY=( zmm>~8GaZUN`Xq(Gd?CSy;lq=dGE8~19$YjYjJ%qIE*kz=OJFics0NH`z>zFrEa6~G zHp6X^hGHl@R9DtzeT={YAte{j6-dOmSb#Bc;OguF35lUWWW>QBHG%m-f>2*bNG$a& zzl1rPN9j9vyT!Q)4+ty(65{cAjzolu;1Py&Y}Nyor^w7NfYSzPMrX|m24~UiLy#hO zk)QS)($n43Pb&^7^^p2&6D7mk!~C?$l1c7Ke&~3#3RtsZBj>Acs9rqZ-C;O~AcdGv zGXrNIf(*ujwP;0reJrzF%!8y%Mka?Ml`+b+ND_FHHCUW%!XQY7J;PaB9nwT-;)GHo zNOPJwi$c`^s|Iv!9bgTF)xb#?yH*1)3fBNy4PYT0suqTrS4quj1SzJ8S(HTHG;bCq zkq_oWrzDD^MX`_=hyz8@G#E-ODYWBxtGZjHN01S;2qVNM$Yffw5fYRjb1iZmQGe`p z`n7-PXrCM0l5LUgLXnD$iyRP%AS;1IC2&Fb%#)slH4|n&Fn&jlqX{~mAmtpn4qD7v ztb<}iG4l}f-JRzTBClNxgoRnkSjt1o1Q}xxV~P-}1Z*qKB|*!ut#FZm(##Y06d*ka zQi@9jlxAUgn1E6t2~QHB1nr*6QY!afE6k|K`Mbz3G5L@vBFAfE;>q5BwL5S zAu`hdY-pQcN0fv5Ma9nFGK|*2WhDqwf_UVnE4}2lf`23%hq2jZIZ?4?liB_hssMHs zz=?{j62Qv(Q@9q`*8(weN|5!yvL4v#Vo5#lqi`c28UYvKtt*EVKi;=2fYCJoyaDi$ zA^+kNRhUPF#t>s2V~3JNW|;ez8HNn@XODVsrV01J;wZQZD=Pj{ZmAWDw&C@fSF%af zUg%WJ6+57YKxXJvGxd;5Rpi)++lHm>uw|pTQ8q|Wf=tKLO{oJ4@B&lhfgp?VVl6s7 z6}hfPxT+mq6mPD#4b^dJc$$ubt@u{$7OOJ#=4UNE9k3__hcHH=5TpgiLI*_>kw%nl z1!skhErm$Yv902)(y^`Qtk*$10A~l#;?tuKKD<-(c0h5W+0oi`O9$GTt6N&T;Ia}J zDS;&_fx|!A-CoI?FX%(XnF=-~NG3m1x6_%WY)YOoewj|6D*>(qhR9*!>Ek_}gAXsk zsTz;P({ELs+qQl$dWDgYNLf!5(} z%{LkCcHwmuba;OK1Qt*%5(lC)V6)nK`%=v+nn~-dp?2f#%+TZj9 zw0wR6gNe6|7o%nP?Fh&XMWK+}Vh8n9XFkC3n9a=QP^3!0tOOiHBgk4{P)m&oO_74? zfgBY{(~HhZ;6dRgfHwg)!qIK~3fsQJGqAcc1DP`lKTnaJuE{<>Z*9JZ=EXxNOOcVl!raNm(%RNeXk~1|H#6n)xO45L I4jeJ|f7$X|nE(I) diff --git a/.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx b/.cache/clangd/index/fsfile.h.35B524E0BEF369EF.idx deleted file mode 100644 index 0a4a0068fa6600443efa056c4672cbb73e816b78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2786 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 J70p)y{tqhwRv!QW diff --git a/.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx b/.cache/clangd/index/fstype.h.DEBE4E158EC53C0F.idx deleted file mode 100644 index 0999c49a3d8621344bd2e9904e017b0e23183e56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2320 zcmYL}4Nz276vyvAaDD8)clW*B$H#slyR3XIxiu*hSd3_rND2}$g*x~VDjEs9I-;Rc zgMuR#O6sTqIg+Dks3T62GFYajQ8Q+uhB~4snT}1Gt(hO4d&d(y!=Ii1|D5w4m-{<; zS(%w)7$IZc$*L$VUNAkB5Te3QV1d8ncliB-j*yB^*XL%>JNmrTopYi5&@aKunHNgy zG76#&g-xE=^;hymGymXf%#W9PYNmAoo&x}ebYxjweMfI(+?KA^eV2M>2Yb!`P6=tR zT3OTk^!}tR=hIF%wm2R{A8!sjm%S||HG0v#+9?&z?6ZSM?7ZAm=q~?k(D=IX`muS< zQE_d${Fi^LG>_i)I47+$In(j?5q((W*0cII=A7DB6@K*a)6Trlq6?fmsoUP>w@(Ga_0T?t$;F8U*NY1x(TS18dzY`55LTreIn zl4e%uK7Ne~F%Z*en#cv?g2pSwPsQbL8;KpxEmj|E<}z?@4b}Z=e-)0JW+a25AY30# zxeOFcn7wSn)F-90s1OPdFjKR}<^Kni$6ab$J8Q0y*r4C3cbd5j^xH2C+n}4#F`o)i z&>yeGM{&Xa6*p6kAKc+xqzEApYqA!>1>>Fb#G=|QmseBbgxIFqG%gt5&fb;qu(xCr zCE*Y|R7V6Cj2E7|x5neh+)RlCv0HUVal!ao`>N4ZZ{2UALL|fqa)N~m#+6xRhFf{1 z+o=!%v7#!GTre&;w&LK4-1Y;MxFNPiTJ2mgj#=Hk_S)q0UkQSl-%&!S-k_JB9So`b z`C_+cM1MOK20=HKsm2An8%&$)I<{4QPlYgu6V*g37mN>NuX|KM^LwZuLTrdOxVT^( zI&?zyiC|Tb3Lc1!ic#f)ap&Uw!Ff-_$5e1ZY*I{4E*SS^)g+dbeFCwO4YwK`Trl<* z&k9Jeu#i}tfs=}VFOmv2!xU}g6Kn=2+Q=o?3`(?-N3a=;Xd{PUGYHW}{=jDNp^e;u z&7eaYc>|llhBk5rHiHc9(GOE9`x<&ObP{!l4h1h9Vh7cXIjlz9z-FwW-RT)Jba>Os zsW8MY+RgYVWDlwtcvy|Zg%~(#XiL=Mu_WQiNK{MEh9Nx#&nSdR3_)x`_P}P~p^aT& zGvLrp=&H9K@Hv;kTS}s2!c(nz@ZHV)Nx^EcO`^$!m#1RsnDPFh7i++lW!aAZ8Uz+* zGg@)>uw!1^_8WKBg-TQr6$R0fs7fw# z4nx>Zz#-GloGkydvZNKNHMw?*4 z(JbQy!7TvQYz1KT)%u*mo<-+@Z$5UJM+?TWOfo&D75%^+wHZX)O3LJv} zX0X|UKpS@hdn7CdwEe~JPG?&#;GY@ru`ebj{3FnqJ)e*5b*_;^WW_{e^a)W0zWxUg CNUoaz diff --git a/.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx b/.cache/clangd/index/ldn.cpp.393680BBDDA86121.idx deleted file mode 100644 index b21627a0c2187d43d7a4142a47d69ded45a37449..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8148 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 diff --git a/.cache/clangd/index/ldn.h.1D557AF101A575C7.idx b/.cache/clangd/index/ldn.h.1D557AF101A575C7.idx deleted file mode 100644 index 847968119b7bd7ce2185801e212edcf7295d1a83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4290 zcmYk730PBC7RN6S2zli3f=LL3BrJijDT|OlkxjJLdt7aWu7nY2y@<+-qswT@L6%$;J>Y> z+~%)~Og%dFEZ>GnHn#zj(tq*=J99bhT+wPiD zlIS-7xb5jf-t8x2Ow%SrE$Dl+bzk3es#9EC!x2gWuUy82u>lU_DglRvkEp8b1bMy5VpP2ejx$c~Jboz;vTW&5r zu5U}ddc9-lmU&m}`CXN^FU~b@>zbCoz!JBvZp)!f-*<0%@O9!wbMyVCM$5PMMU_=s zo2Ny-Tazw%`DE6SSBG^AXIyylsy*uT5c86oMJFE}Zh58 z?N(ph)qnYPe$0V6VV0J$4Kdrz|9E_R@LRhT%gnX4UI*hN3--^KbQn_G%st1xn;kZP zv+ee>MW5b0;oV!;asSe(za8!F4_Wus`jy(;sFYhZ{J8L2gFW~3I(LV>y;N~4qVI#_ zewM@G37X8|Sr0BqJ9n?F@A98@vM=ahgkbNV_N9GOQ1|rG`^jAoLh9|No?rdDA@=3W zp89p6(pfE|WffPq`L4Y4tZn|3v>|NgV%=`Oq z6+Y+FEf18NuDd4XIz> zJ}P>{ao=aQzxO=NEL~VM?S_Pq)rw_xw||^!CnIi3BE(7J6rv$4oKHe*1e;wg_s#|p zdk3C*O+|z#mngkxh*S(9mE$)wU%{o*4=5SXHd6XwZG4LEaTtRjcL5 zIORY+LN=SNB&>`f*@Gc_Vdj|Ko^zx09EreyBu!EfVOf^A1km;p`fC^GT=e7_KS(e_ zIYKWQOc43%FYlF%4fJ9d#lZeV80Cf>HSzq>rDW92qdBy^(qkN)*MT4uy80V*) ztNM>WN76C-WOlNgu)O_1c?}iCgEj_i2;`YySRd&YsS^#ZzZY!Y_j;;p5YHIF9peRuJDASi)W(PMBt=IAKi1DhH0WO0vHiZg+8E6dGiJ|XvlQ0s_a~I`@#ni^Ig*9} zIc&}#>-1dKm6c|@4om0AFbqhOrMVN<(T3;)VS2?apG?lwPtW0*u~1;NDq1HRtU#t7 zdMI-G4GTwdaQ!59l8Uf$3Goer^-hnR$(VaVuX7{{15(%&t(ZXke$~|;d${!n^Eomc z1I#k>|5fl)g~i}$ur1)pM0X-+C0b52SV1tmdB(hdJuTpwWGGP32nNxh`%c@dd8d2( z-sG7uaF6sF=_?v^?;U&KL)kFPYzfiA;WjuJ)S|Ioj`GE(loDfSm5iw2_C!b`RHCs4 zd>#|C&#mU#3OVtD9h7!TxoE7{!#}j~YTlE&YMxAi`aM_=rD(8%ngTb^+2dYc#gj;I zyF0n7M1$_Syah9o^=>Vr zKj%m^jyI7_g!=>6L!AFq!>RZ4ni3j#k_7{b=i;43g9#$9zJKn6c3rWto8}djDY@QHL*I;U4T;K z+sD=AJm*OWBsVEUgKknXV`gb5Q0~}yJTzQ zC^$=9`Yo;nPMEh!l>6G9PWN<@?qh9FZCqC712jAQ%49F>F<^o(AL z2c8o=7!N7FFPz}XcuMg&a)KY@Cq;g6fCaH_i#K3DFdrC?hdJ zrnp2II|Z3y65n+sB@L6w!w=CuFO#b$D|)AxDwp zjt38+2ni?=u~md2B%lz)oe+T_Q}m(SJM8i1YqdL?;pXN>=f>k=PO#`Kez>N!THoqv zoO=PL7cY&MVZ-=_KmnAPSO7j-AX8G}P_KU4tf{H|trarq7@Z?l&k07x=!gtMlEPYw zQLGXP3dK`$qKpp}L{n&D0r*CNOks%yU=ENeBvD2_f=uCv-yW$5GDRZFm;+=ALj0=3 z55H9ybUmgICgjez%kUH;7$KTc5$nd|2QsB0R)QdeE-3&p8u14*#U9ECJ=la24`qCk zL8hcbxn#y$Q)#iKCDThM!e2&+Hut`_=~(qf$(Ygcri3^$274hS-ulyI{W;qDGr{^Z H&ieDe(n99s 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 b0c8ec756a2ee99d7084662b952ec6a13234f371..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1072 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 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 e5ba05e89192aee896e5575bef4a4ef9edcbb143..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 934 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 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 54941a3a2d217f3142e55cadbd28fc15f2b79951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 758 zcmWIYbaQ*h#K7R3;#rZKT9U}Zz`!5~#Kk2=nMZ*14MqlriaBd1Y|J}sz~l1ORqesW z>@Gi7fh&5q&U)x*sVQ|bxmupPfBcj{N$!QGeW$A}F6=1|`P_MN(SjNKI;{ON*kZPF z%*yYYw*J_}<9rPJjJ(=zSZvYR({PmaoBdygd42^STrGn*9ST1kRa(My^Vc`Q$Lms_ z8m!2ER{xQAd&|z5!Bw{`GEHMVCL5@T-8GT?G__>b{7L`bE?)aF``G;hWj4FNDu{Sw zt`v^@(j(HBUL(uDWl>IrR_ynVU(?>MNqr}}Z`sr0%G@N7U#dBJY83mmn3t}9paBQwvmrzQ;x^S^ diff --git a/.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx b/.cache/clangd/index/threads.cpp.3D4AF726B9B95CFB.idx deleted file mode 100644 index 195e416bbd5f058e792eec502a0844a7b1e3fe90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1834 zcmYjQ2~bm46#ZE+3lGAQ@bj}lk`F;4fCxm9Vgn6GvBR*qU`wk4S_FzDfYd4!+(sR! z8x#eXA}(mPAShPjXed;0P^c^;sKupJK`8=?3#fg;c?o|e|K~2}-gECeFDfh~M8?3d z;DoU3)WnSCbPU66;ZK*LO&X(MSPgV!mz~=im)7IO-SRG4YtV$R`RZck0hwm?!bJs^ zeq>44+=OR)8unK!bLq!Ujt}Y%6LBiOYR`)Ch9ObXv<2W>keI5l%( zr%B&L2D!S>wnp=#E*>f2hIpzAs}!I+m)r+{T`JyKuPd=qE%x^1Z8(R)E>_2)L-*S zEN&dTmo4U{v5$xMhN#tshe^ve_ILW$N1nf*Q3bs4^GCVvJmUcsa3yuRUJG z=o1raT9mLV;mo-NU!GW%zk6DLjn(LZp!}LUcHUtNHCq;UYqIjHM)fCR=C?oaeajJg zJbK;p^#$KvLvrA+&+djybd7sF3T=wx<^IybZy3oLTAB4jO{wry)RCz9O{_O{GcRQh zv`6RTBi#3%ia~AR?YG_fbehG}d#%{uJoo;2R~E!1M%LB-S02zCdTH5e-{kHS^gk*Y zFCML>yrXB@l^5&2PhC+8Rj@R!VsB`99uuc3-7pdmKp-QS1Pf!r7<^cFTc3F5zQ_ZZ z%M6TE^VAX<&xS`}EGh+4NU_+naewuBbRV>#xEL5?mf-SkwTp$Xxm%3etWG; z*pldiNLG%k9R-lyj@}9c`}x_frsf%2jcy4;!a*E9yD1IgZ<@=8Za1czHDdm%rLQ~8 zp#KEAl>((RD$>z2AiUm1(@+5^W{6EiU{a_u0$~vm3qZ1M*b+1jAOQ>DXtB@|09TX> zNSueu5hp+@tQ8_uPINt@`kS(`FX@1ka-@VgNp33_nk|3aUy2}r3?KsJ$TFl*+QK`k z9fF;tbg7lOlRxn1Axwl@4BcT*d<85i;0l~j(z5pE^x^eM zXCPKimrps#01#m6E8cK(%|H8Z)IzK>M!5v3ptP;Y<=wW^dgyj!IGXwn`;6@CE| z=Z<4I8+8ICK_{l{k>C?DbIhITZilD>QjMwEXf@m)M?+fg>}xbOS1RYABm||hCvOhg z1t5d*U?+slUtksAoz{Mj21pS@G-bM3oLQ#n0O@1zV>&h@Q&`t{<Ye#cZiWDpPFW zRvhh4!-o$>1$p9g(8O>(j_T=%iZ(H2xsjw!8?W;;{*f79A0N|w#c1T;yQ8l!Kilh0 ybC$_5Im*DW_|$|HDKsUHVhg%8$I8*c#vUj5c1#ut?ITW0V z;3fej6Gh1sbw*?kh-6_ikzf{GK_H^yG%WstBIsngO&53H!vi+yx#>CgeBU|ud?!tb zi-~EJA{4bct}rjF!0v(&V#znZz>%{|gwRe3p~AYFl*G@e8S1X2%aPw#=>LtxTT{}b zhov2XapZI}mi)&2m?cG)I^VcKAWvyp6`Hc1MZ;stw9=^KqzSE`ZREKCtGmENq z44|r3$?zY+@fikH_rW{irJG)tcQ2|rlzQIh*oG+E@q`ylTyEm0c^eZ#(&l&Q4{zOY zbag= z^K-qts53v|WAX7jQO|NaAEQ&Hc$#t=Cm0BSlb207BO({7C@&KB_w~042Ey#% zzAr0Ut#MMyXhK*g*Xad==n3xH$L&LY&Wr>l(wZhbSfY{W_^`XH6dA81{kXF3 zrc@-`SLy2}n8|RnGlqWps9#NKAls-hdO>(js{OkwiEGByloG<;N^cgztflj_%{uKX z2I)!VtcEoU1}eWfPtlgR;Ph*X@|u_k!eY6Yg^9`6+Kb}79RD%Mk7Uz2+E*|_wndH{ zS=sq}#8a9Ri3F{I3_xolxu7v{hjgqjNL~}F6)~8RF=pUkD^HJ|wM9h`w^SiD!8}2c z1ca9(fPfN-o0lQD;iTk(@}NBi8)$JhoM4#V{#omoV`nhuiDB zY|pz^>GzP^oQ(^E&Or9$Hm`Z8Yx<=ikCP8atHtCA6_^7|2D1v5d+!Mq90y3SA*4!v zGXSY=QFrjYWg{mEFB`;$LGJUJf7T8htG~!M?xIyeiwri(jBe2Q<+WGe{L@s?Npd9$ z2?sMzyDu5osrI-;+&Bo&f#RZ-=X8;A3!TJmli8FoQdmvWA-|VU*X=X)HN~61x<+m* zWC|AM1k|Jpena6lY$$Q_n+a~{hG6q+32zNx#Le#;xF7drh=1>V`X(bi* zZV{Clq!nV8LQ;?*rkIuXVr4F|{9u-5MCK?;v2*TvsssD)-2d}(&UtssU5LfG!>L!Eq`$r>@yVb|#bYD(I}XUFO5 zot6hS*SqqB1XpR;%^0TxJ z+-q=Tbrj94D;#TNik}{%suFEZ?VS*l@k_2jOk?2jMfBcmJBbsM|r%JJ}D?i2j` zfauu7lp}%pmPO@-{b^^GT2y1#7FVXx(syfAckbJJbKI3J=QR62QL|TcwkQB;bV;E1j+*K z_u9m{;{N@kcsMw0)|rj6K$x54S6wUTpRjvNTBQ35LHKJA) z2s`ZCot^*jqg66D7zuEr)gE3RniTNDHh~4%zzN#~(w}4s_+XpB0UNj=wTH(apJgP4 zq=AE>HBKU6pq6FK3bO!{@h^IkJ9M%8Hewl*-t_le+)0$oPy!I~EeJud0UovqIj~o9 z4dU{cHC;5z2tEP_Lk5y~OoSlZ4hWJLVtmj9?uTuH4eSv{*B3W(>l5xRDnvC9f-`V7 zp$NSpjBP>>nvC|OdiA$YPva&=g^`ERJM3BMB*p2-aLytdB@xf^h7E<7xcS2`g9~IGdKm-H@jUYilp^95X#Em!zf)Pi7xI`3Gf-K?K zR9tE`MyqJtFa?a7DsH&cC2Fv)Sk&0W9IKFOsX~%|_nKEvIfrkU_xYFa-`H zgim|E%6QZ{`_S=)Nrxh<+Sk_XIuMxZS8M<3NxOI7-2F<>HSD;4 zICtSW^SI0x%gS3X*zPDC_if?geDfw7j;r2Z)*0~bFLn~^6VAyIh6q^=oE@DQ{hSX@ zvSc}B3KDZCoT5+hRRiz?8=ni4Dk>c%=16#neu=*tfcN(#{LuCNHYbq<5%$u0>D2(- zeP)mO>`&zeiRr^%PbbG{V+_D;&rdwQaaV8?Ss?ZH)%v=s0r=zjPYynJUf?CN5W)ej z0Ul}qo?QQh`DMxyS!BM1WhXgE4Zxr0kNWJJhASqKnFxotgm|d|c*CAyD@Is0_Z68B zVWXchQVqb@k}me1s@Xq4Voroh^d&|$0N+b{f8iPP%qSgWSXh`4rU$l9A9rTT&Z~nZ zCQ|Q}ynZF`|Jw(e^eS_#2xw185?McL5*QKqt{Q+R{WB!yKMPhSNz9Y*7{eGpH2^nP z$&YV*y)0E^0ffD^-ri~eUSH%~J39G!n#3f+(E-t6Y5;x|erWZe?&L`#izM7v?i;KI z;Pv$}PElu`PM4Sm;S57YfEs}3h5Zn|XZfi2C8ojJCu%3U1Ao-%x&7L!0M0wy*dy3q4ZzW+A;RsJ_YO+T1*yfk z#(4t|Ti>y1T*JQzYpM4t-n5Dz@Y@HP#N3OV9c!O+L}bC##NXiWt_I+`%LlZL{Z02J z=7kB;v}u9BPdm4tJKeJ7fyCT^)3xa?z!85>yLC6Z<(bI5X>dQu&rJ=$nmc!DPJLE?yw%E6|sHL5+o zZB!gyJQyn#)V+SOk?+ZU7&#{{~{&a3zUY_lL& z@KObD#P{z@*VN75(>jdK!zdWNun<%S8dxa}7#3B5WThlPMl~Q=DFu*G1xQv(0A#p7 z$%^$uhU=58SUzO9J#Cia@{r;3BrEO?8J~3IinR~lqQ7|J)!-iC|27&9Pctd@jy`a9 zk`-Hrj7vtcV&{ z#l_Lcaw(|uQkJ2D4y=^dm+}F)WaAHtBd4`y*78ga7sB7{2wb1?RNNlJ2=XG`B6s8o z@252?#*aque3BKzhYYW$^c9bX43DSQiorwPnSOY8)7cYSZ3NjQn4Dl(f}9qgmI&(< zex#SPaEYo({86s5O~=6KFA;lkho zQSwS@LL(e9$x2Crj3Pj?Qi>oKWY3u)Q_A?x{pwh3zH%~K^JXhG{n+Ov7mMNx3$hBs gl*JjbLXOEx53pS|t-l_&GWz**{%-*1_x8{K0XMg~Jpcdz 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 c44e358c71f87051ec93b64375c16eb01e6bdc4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmYk7e@qi+7{{+Kl^@+pdv~RG*On{4Tt!5w8@Mux{IIYx%T`dA;T#(X+lZJftr%e0 z)Me4kxsACWxJ9#JWX{AyXXe-znNo2isFOfQ6vDvqhYlrkVI+)Uw&z~+Seo!{?)$lW z-sgSq`;^ME($dx#j$2n(*1WxbM~$B2I061bJAw^o;QMhM$2A}B{IJImcG0~v-pk!x zt9y^sJ%6diHdHF#9vRHpF#cQ9MD>Mr;l69_Nu+ai!1+z)tdQ|%<+Tq|?=Ig|y`?{3 zF4*_!s>Z>#j;nVJO$9~fKYYJtKGC{i_Sxa8;i^a1PwGwIALIY>R2&|#m>yd@Z;ij& z{Mu~X%JI|R9z3_z`q9|f{uKIl*the_(DY{+!4pdH+^N71qid^2qhpVLa=Oyq7(UoN z5uY?iekp2547F^peV=e#M#(eQLqGKyjYP6=qQWZz6D}5Z1)o*D3t7?0N78r_1K20~ zl9(VK87%Jn`ptT$k???RD{L+%hB_syJfeX3F32k^>5S(2fRjN25jZ6 z0u#h{r`rZ^{M+s4NjzY$;!S3P`1V)*rOuA7iWs6#;KT$&f|&^y#~Rm(y1aL}6jHh7 zC7vvS=Up~eDieG@bf$6lk3#FKJTU?GD4s+nh)cE&zjaA}JjfFr;AAD)&IIw~{bjox zbkB|DJ`V!_|LZ{|epcSfQBmU&rVf_UIi zINV|Hxxfu;}X#WR;g-Xg80Tn zL06!?=@w6-0B0(hDig#lLxov}m*em8BpPr)4kR-{%mwRT3H3!qQmHqvG~%nKqFSTY zVhdC(kxFDt2^GuKG6&{0JHOX(y=8NPj*2d`%ZgDztTV6}V_l3f*jk3s#`uD*WfyIX zEi|WP7Hw#5o7xy@u3RdY@kLUxLalJ%?y+U4tECzX;5RX_AeVkAU+J08U4Xi9$_Q~M z@T`_-v@y(JYe_~MLkzZ-V6-v3U~9=m8$$~l(-Mm|G_Xm1F0!#4DM!W#QZZM}b>Kl^ ztaWiL&R76X0hZIUj5gK)Tgx!o)??{a`SXDkl8#qN0^r zP554*-$XSj(yzK)&0KV~M$KJx^_2Q_epe zxG+vK9H2JFSiluMDpuU07j;B*P=^(59BjB-n`*T8ytjGb{P~d)m=x{q2fs!fhwH=L zi{pp$4YoGiSOm8Owl>sg!#p?Eq(aGuhI?jx-t{S|;h_YaZ=!L1=#TU`6M T`rt#v&&NE!xBo3q>jdt92^C8E 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 a333316d3b51b5c1adb3af6398ecb583eca76c91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4508 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 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 44f18c487f4b6ca5b6d01251fb1ee992b3129dea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1660 zcmY*Y4@{J082`Ta=seDskNf-Gecy5Mo?IP>a5xGFrez5`XqjM+Ky#8JWAdlCTXB(1 z90N;>qAh1CCgJ*{EVGQ3`~fTxb)@Yec2~&@5|eBySmsl z`^UG_p3mEk?VPI{cNQL-QZKt+Z+t#k2!`vQo`EjjdUtqiwwf^BJWtp=aNE>$MURzqw!c1fys56yJF9)QC#y>&VgcG^ za#=Yb{eJ10l993(fu=w!CPn6e^as&EZE#;rxryk3UZgC#hXd03yo#+UudE14WIoWU zMRjsOx}!2Q@LXl&fSn`&ZTHx-I3Qi>UNROqa`TWxtU$ZXZkYqpAL=i(E6X12u@eHc z?2&UhAiZVpLrO_><9jkO0L>_~azOgCB=3;FccRZk?gHBH^m{oVeY}qyG-Ry!R3gbh zrM4)XRTQ&!zx0e0-v;KVL4HMA;?Q{4T2c&g>UVb=r!PU8W z#9$I6i_v1{ASCkXiIUhjVHng=k+l-+5}gHV%~rS#X$Wg}!Z11s!kUdRtn{W=EW6eI5v7vNZDZ&dD%st3 zhV@ZNcFPRUqmn|EWV}Zzxu`1{2Lm;#%#9_$%EuPQt?pw>;#P$$5VvX*Sx4+S35$GO3O z@}r=R8&p$uUDX0ejt&%1gJ CCj8O> 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 88e3fa21cc8570d7ea039ad9aeb21eb6c23f734d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3832 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 WmnD6y;pL+d+Z#n@#{6u73-~`_y~>0D diff --git a/.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx b/.cache/clangd/index/util.cpp.7798728B5E2F0E42.idx deleted file mode 100644 index 0b7254ca30f7415f3ba1bd8af6d0dca6a104ed00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5358 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 diff --git a/.cache/clangd/index/util.h.4B818D27750FB9A3.idx b/.cache/clangd/index/util.h.4B818D27750FB9A3.idx deleted file mode 100644 index 74af23724f1f79c69a5d89f621ce2022b6de4711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5730 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 - - - - - - - \ 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 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 #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 4cf2dcae4bd9ca356c5b5178e1017bf154424ed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~F%H5o3`K3|P>H1@V-^m;4WbH8&uWIwhEDnJFO02QDDRA5F5 z#5vxsX7o&a6e>Uk=AnRn9}?V{gQe0x9SA-GfDOuSSojDS;a)h^^03>!4ui -#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]); + } + } + } }