From 902a3a315eb8fd14bc8cadc827300f90f43a5152 Mon Sep 17 00:00:00 2001 From: Mateusz Piesta Date: Thu, 24 Nov 2022 10:28:37 +0100 Subject: [PATCH] [MOS-806] Prepare scripts dependencies * Added scripts dependencies * Added script to generate scripts package automatically * Added integration with PureOS CMake * [MOS-804] Created factory reset script --- CMakeLists.txt | 7 + cmake/modules/AddScripts.cmake | 18 + products/BellHybrid/CMakeLists.txt | 2 + products/PurePhone/CMakeLists.txt | 2 + .../gui_image_factory_reset_failed.bin | Bin 0 -> 36002 bytes .../gui_image_factory_reset_in_progress.bin | Bin 0 -> 36002 bytes .../gui_image_factory_reset_success.bin | Bin 0 -> 36002 bytes .../BellHybrid/gui_image_update_failed.bin | Bin 0 -> 36002 bytes .../gui_image_update_in_progress.bin | Bin 0 -> 36002 bytes .../BellHybrid/gui_image_update_success.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_backup_failed.bin | Bin 0 -> 36002 bytes .../gui_image_backup_in_progress.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_backup_success.bin | Bin 0 -> 36002 bytes .../gui_image_factory_reset_failed.bin | Bin 0 -> 36002 bytes .../gui_image_factory_reset_in_progress.bin | Bin 0 -> 36002 bytes .../gui_image_factory_reset_success.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_keys_failed.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_keys_in_progress.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_keys_success.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_recovery_failed.bin | Bin 0 -> 36002 bytes .../gui_image_recovery_in_progress.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_recovery_success.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_restore_failed.bin | Bin 0 -> 36002 bytes .../gui_image_restore_in_progress.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_restore_success.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_update_failed.bin | Bin 0 -> 36002 bytes .../gui_image_update_in_progress.bin | Bin 0 -> 36002 bytes .../PurePhone/gui_image_update_success.bin | Bin 0 -> 36002 bytes scripts/lua/backup.lua | 16 + scripts/lua/entry.lua | 86 +++ scripts/lua/factory.lua | 39 + scripts/lua/install.sh | 36 + scripts/lua/restore.lua | 17 + scripts/lua/share/helpers.lua | 202 +++++ scripts/lua/share/ltar.lua | 233 ++++++ scripts/lua/share/lunajson.lua | 11 + scripts/lua/share/lunajson/decoder.lua | 515 +++++++++++++ scripts/lua/share/lunajson/encoder.lua | 185 +++++ scripts/lua/share/lunajson/sax.lua | 719 ++++++++++++++++++ scripts/lua/update.lua | 11 + 40 files changed, 2099 insertions(+) create mode 100644 cmake/modules/AddScripts.cmake create mode 100644 scripts/lua/assets/BellHybrid/gui_image_factory_reset_failed.bin create mode 100644 scripts/lua/assets/BellHybrid/gui_image_factory_reset_in_progress.bin create mode 100644 scripts/lua/assets/BellHybrid/gui_image_factory_reset_success.bin create mode 100644 scripts/lua/assets/BellHybrid/gui_image_update_failed.bin create mode 100644 scripts/lua/assets/BellHybrid/gui_image_update_in_progress.bin create mode 100644 scripts/lua/assets/BellHybrid/gui_image_update_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_backup_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_backup_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_backup_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_factory_reset_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_factory_reset_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_factory_reset_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_keys_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_keys_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_keys_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_recovery_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_recovery_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_recovery_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_restore_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_restore_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_restore_success.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_update_failed.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_update_in_progress.bin create mode 100644 scripts/lua/assets/PurePhone/gui_image_update_success.bin create mode 100644 scripts/lua/backup.lua create mode 100644 scripts/lua/entry.lua create mode 100644 scripts/lua/factory.lua create mode 100755 scripts/lua/install.sh create mode 100644 scripts/lua/restore.lua create mode 100644 scripts/lua/share/helpers.lua create mode 100644 scripts/lua/share/ltar.lua create mode 100644 scripts/lua/share/lunajson.lua create mode 100644 scripts/lua/share/lunajson/decoder.lua create mode 100644 scripts/lua/share/lunajson/encoder.lua create mode 100644 scripts/lua/share/lunajson/sax.lua create mode 100644 scripts/lua/update.lua diff --git a/CMakeLists.txt b/CMakeLists.txt index 79d021dd735ee9d417cc6fb8986bbc04c71975c0..d3295104c1930926f7fbb277b2a2f3af554daa04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ include(AddPackage) include(AutoModuleOption) include(AddDirectories) include(AddDatabases) +include(AddScripts) message("Selected product: ${PRODUCT}") message("Selected board: ${BOARD}") @@ -192,6 +193,12 @@ add_databases_target( DEVEL ${WITH_DEVELOPMENT_FEATURES} DEPENDS create_databases_common ) +# Install scripts +add_scripts_target( + TARGET install_scripts + PRODUCT ${PRODUCT} + DEST_DIR ${CMAKE_BINARY_DIR}/sysroot/system_a/scripts +) add_library(version-header INTERFACE) target_include_directories(version-header INTERFACE $) diff --git a/cmake/modules/AddScripts.cmake b/cmake/modules/AddScripts.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e599d6421187a8aa650b44fbb6d75f32335af2d8 --- /dev/null +++ b/cmake/modules/AddScripts.cmake @@ -0,0 +1,18 @@ +function(add_scripts_target) + cmake_parse_arguments( + _ARG + "" + "TARGET;DEST_DIR;PRODUCT" + "DEPENDS" + ${ARGN} + ) + + add_custom_target( + ${_ARG_TARGET} + DEPENDS ${_ARG_DEPENDS} + + COMMAND ${PROJECT_SOURCE_DIR}/scripts/lua/install.sh ${_ARG_PRODUCT} ${_ARG_DEST_DIR} + COMMENT + "Installing scripts for ${_ARG_PRODUCT} to ${_ARG_DEST_DIR} directory" + ) +endfunction() \ No newline at end of file diff --git a/products/BellHybrid/CMakeLists.txt b/products/BellHybrid/CMakeLists.txt index 82eacaea05d466ea6061444b3e3debb646f2f2c3..4466e13d031878a6dfb7b82f44bd83b556950561 100644 --- a/products/BellHybrid/CMakeLists.txt +++ b/products/BellHybrid/CMakeLists.txt @@ -96,6 +96,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051") SYSROOT sysroot LUTS Luts.bin DEPENDS + install_scripts create_product_databases create_user_directories assets @@ -111,6 +112,7 @@ else() SYSROOT sysroot LUTS "" DEPENDS + install_scripts create_product_databases create_user_directories assets diff --git a/products/PurePhone/CMakeLists.txt b/products/PurePhone/CMakeLists.txt index 851e41d55128844ae43e7c9ecb5a4b106bb119f4..1b7a150ddefc896370604320bd7ee569e4c1d117 100644 --- a/products/PurePhone/CMakeLists.txt +++ b/products/PurePhone/CMakeLists.txt @@ -121,6 +121,7 @@ if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051") SYSROOT sysroot LUTS Luts.bin DEPENDS + install_scripts create_product_databases create_user_directories assets updater.bin-target @@ -135,6 +136,7 @@ else() SYSROOT sysroot LUTS "" DEPENDS + install_scripts create_product_databases create_user_directories assets diff --git a/scripts/lua/assets/BellHybrid/gui_image_factory_reset_failed.bin b/scripts/lua/assets/BellHybrid/gui_image_factory_reset_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..94c1bb3b98ebcb8274614d2a5ba6defedb38b971 GIT binary patch literal 36002 zcmeI2!EWO=5Qb$0h%bw%4^Wh^&|?paa37_Q(ZfP?kVB4s1iwk)AV76%UO@{L@Ub?0 zp#g#Hkd&pFZ30mgTkHZOAj(jh`7``Dq9jH?6qYw8zyz286JP>NfC(@GCcp%k025#W zOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l34DhHbg|BqwJC~1~WPx%6-(a zmpQa_2`XI{T8(lEJ-FS{?a>Py)lwa6jq4xuQ21Yqc3Ot;46vF$k>^DT`QVpc;<#KY zpaNKOsg@DF!eAsTcIq0sy&9>%M?_a>sY>?C5II`Ip?O9Czv=K%O*%2P|O9{2`1))xg|*9QNyE| zASFB+;ZXSBoYh=7wW`iU)(kUQu>Oyk1FbWuGg4o;zO?$ zd=(M5n8ukn(H1#1-Jp5U9H*m+2c<*F5y$BU9Wy$f)6v9(RuZi;;yB%)^-XJkI+}Rw zNXsbFoY&Y9_eD~|NjYF1laa2OxJeiH1V36iOS)a+CSBYO{2aaIchE}MLXTDu*+>^I z+%9bkoHr1>S59H)c4=FXaxzXXgOux>>*OM-P~JyjjYDrOEq;F}b{8%>c#G@b;pin9 ztK$8F6zKSO!ceTU1V-l|1-*klx@~b*z&w;y2B}S7R!gL`?-mG!va)ni4JRY_{bb(A zP*#>s^^qdMt(Q9f6v`@tl>V>|(#x~ziZYj!DMT(QTgYru8}%(p=}z8V~J+G zaB^JE87G-AA;V2D+)9Y31^hZkM+9yG`4XyQTXP;$g^ zxV3q#1W){Pej=QdC?Tni8vGEq2S`; zkry6+jgBXnxGHJA&KASh3cZSEOW|-VJ6i~DS||o{Lx*EcJZg9}W8;yZkz+%QW|B3= zW7Q<7u~)dSNm3(gofMCPz6|zg4Tt6#8UFz~5*n$^OJ?vkqBjR)I5pcS`-o1?4*fdd zr9Uu2``RPA-SnQ%(Gl-DX8Z^tjB%AM}sVtOOgb6SKCcp%k025#WOn?deuLS(6y7?@vy}nhZ{`qc{TEDL`{7>|K z7x>pfrQo#B3YLcGYNn5qZa+uL3Brogds0zfr}SBYl7(foZ%_(WVRB`)T9%FxcBLZb(O9vgkG6SZ6thLQIz{qui%4*JJs3qo!W@~O|v%e z;uvLzM*gL7c z5?dqdRvQ^B)pk`?saMZRv~BlUZM8D$petEhxjkKMq?9}yAgQaAJcE>d?G&T~<$JJl zce&X6>qjbKG~V8Km27Ks4^n@-tc*Mzy5DTw!O*=zdM!xli~W6`6oB$lR%=^BbpWS9 zDslv`Sq)MRm9mgktkij5KnDxyvnp0*1*-#))lP)0Aj{aglD(AZrSv*TZIP6UD+YPC z9{biw!K~%hn;ffGdClr#qKd&7*#@b)mvU>XPU_E5%8Hn*>>zdMrCO`J)E{ab!CkD> z!FMo7L0y@TUh1y|slM8sl(zDGFi`E#!N$TxelWh;qq5TXqW0IIti6wt7NQK<5u?v3P0I zNoCsJxF@D6cPcgVUvWS_*OpUC=1R)K9g8<6zyz286JP>NfC(@GCcp%k025#WOn?b6 z0Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#WOn?de K#{^V9{eJ-c;!aEe literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/BellHybrid/gui_image_factory_reset_in_progress.bin b/scripts/lua/assets/BellHybrid/gui_image_factory_reset_in_progress.bin new file mode 100644 index 0000000000000000000000000000000000000000..54212a21e27c02988a36c86db35e4c872362b9fd GIT binary patch literal 36002 zcmeI3y^iBH5XZGJaBhO=$~zzkUm@iNI4tu>0n%J?IQp{1HUW%0fgUA$aN#C^UqK5O zZf&@s0fv(qQnE-`_KKp43o-;&B1bbnQGb4z`q;MxaAE~m0akz&UxO7|kw=r)P^cO3H-s9W@EjI)_))U2A(ZX|DtCt_1?)@NC!vx_&V{KJW~~Lu}wOG`kBNPeFb} zb`!*yAU`4-wp!SlBTyoU3Hw-J9-5V*V^jza49Qy+dR1W|H6P0p3YSWPxY5GqOh$#{ z1|VzX#Jw`?ugSGKMiFr-I`n!W4vtH&M@Jt1l5|lzYkawpFdM;?;2atvG&5z4k zMX0!IkR>5GlxbJUS*RtQRu^h9r%c@U$&!#N^2NpWeny#iM3N;TRpk3D!8+ZuP?As! zg@2R*>9*rGE|zNatOy`!Gcrkxv!v6WayckB1-lywEePc{lu1ucXG5dLZJth!Palc*pC|a{G@Gq}c6#;4i;HFa73S=3RDhk4T zCI4q|nP3>RW!5OPMjrp%yrHr_me?^eE$}_dHbm zkRmQ6Rxu8aONpph9e=rrh}K=V8Mj|YrNlzq{Jv`>B3gH&+8phdVIegikGd)~Jo1-e zDJp~q2IWQIWo1xE&xZ2E!X>l9MvI#>85Q=*xW6W&wn0SZqx&#C#0F;=nhi%z*0{kr zSal~2TeGtc&V_wR436q|?BYXezway7?yACTeI=;>aHqBIcNlK%dO+AOhQ|k<6TSfL z6zWt9&Xkhv+D)W{_4<~L)yUq%DaKXf+=-SF9r?aArFI`3Oe<*R{h!jp`n;`h)@Ik| zG2Hg(T-^=t@SuKAcPHp?;`$wiTPOUu1@7>){uW^P`LB55yNRhj1e);kBwV=9uT+@L ztN<&(3a|pK04u->umY^W|4@PU-fMzOzk%BO=hTBwf~d>qw8n*}qA=Nu^ zg#lB@Le_IKA@y&xtD;;8J-dN=dC?!$oAOc4p7c+j-kW;?^)sO!z7wkzncx6_yf^Rg zTyM(#%MH}-bh1h;Ygul!QFrFEfR)C6wY5@gj$}gA675ohSZ?(V)LI|xgIpU~9uHQ& zpg*h$bu?;ViQ~kW3#^s>q+G2KRp07Y`_f=1?MKzZqds(~ zyBil+(}=-;ySUH_KSKVBieRna1GKf;-Rf5>phtTBD0-|$sQV6O)OFd{_5gofK`sCB zsP`V_I>Qar#{339_H_n>{YoEQt^OQ}I>Lk60S+AO+Sr0kZ{Hf&R3|dg=LpoYntHpv zQY%}|A2$Y3a*e1h4!D!}TpLy5a5jWm z0orje;>>Uh<>ssJOTg(!|AeS}kJ6?6f#$1U6{6ONdiOAcH)Xwt{uZ>Nd}?M)6fhTe9OH&h3L9wuuH& zHTz&ksFWm{-{a~s=<=8P(kHZt=G;A~)OQpW&P9|ew~aQad{SDjy?b>Tm5N$5?-6=X z`jURrQVV~$y7a;yprk}IASYIU6<`He0akz&U#6&Hw-a literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/BellHybrid/gui_image_factory_reset_success.bin b/scripts/lua/assets/BellHybrid/gui_image_factory_reset_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..28ee21d36665387d621b60af3c0b9034ac9b1e9f GIT binary patch literal 36002 zcmeI2!EWO=5QY`xU=&!adu%Vu*Vw~i`6_vgEf7Qwa@eCDfv;jC2Oa$gRp5h;m9dA) zfU4<`lthjl%ao`d79At7W~iCp)W;!XQ~EUlelYNfC(@GCcp%k025#W zOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<3wCnQiOtA%>qCP^Ze(JJntx?4s{Kh&|6 zOK8<=RB0BWRiwH@&-yL)^GH zR0vDPYPF(M7>wD9P1>hsy)pOb5cSaJD#6>3>8zn?nvoCT^6K|qZlX$-}$cYe#R;6FBA=xB` z&J}+ak+2Zw2MMAcId$8i{h)okolPPr9ZHS_-gfAi(eb>UO(JL|(JCW>w;fvFwDxaj zlL$q;HYM&fN0IO-DM6(qai=+Fxo7Sqt+U)ScZyW2bG^b8Q<7<{;q1Pwfug6Uct*

31DfDg?o+m%_rbQ#wM*E;0ZcunSGD zD`MWO|8BNT?eZ7BAOl{WU6dD)sy^9&6$?n!A6ACY0#Z$~j+RR8aJ7jrMrPeP@57hR zM4EIpN5tj~Qk&E?DBC>eeURGP>=-H8b)ef(6P-KfAiJc$?Xh>e)=7u6+%tEQlpxXO z3o~h%;3yJ1i~?y5bQYAN>}Uc{87#rw5G+Jl@0 zHwWk@1@(gZ;)3WV1?{7Y3bH62N-jvi1<^6131`l-T)wY5OUhl9f335m9P4^kzL(@~FgnXSJ;ii;YOhn$ z-ivjF9JTl20@_;91r0_((v^^P*WSo00pZO(A_pxz_bjsL{M}jm40pkY5Egti0$$9e zEb+hw6sgvFHiD6FW?#T`O(09%xwmw58d!_>Rcm}r(MzANcy04R`Bvr*PZL(d}cYwDBxA)b3o21+I zt3uURRjjH6&urwF)JIY5>g4e^sa~I(&#P7S(0&joRqUEC@@2JCAnKjky;Mo{03XGe zl*|rC$eSmr^0($`ogeE~;$s8yaBM(kIo@6ubtUoxWco}tE|u3%)*zIQ#hm6b#my#} z*VrZwxLc%7t;mkBKQvG2eoQLe?;9vjpp-gda#^;I`cVzJZ_txJKwx+9b55`?Ta?<2 z%)vCznzTGZrqt;Kvdr5rfV+J%YyN;w5>;T2dZN}+U(>(lNu}*qlv044R4+AJ441u< zU9ZxbN&)t2T&mn8X;~%f;R3A`wr-x1JM2}N+g_m*R@_L5rdOq&?ph;t@T5lTDlGxA zgNNPr3Ytd&pjrLc552-Nx!S9Lnl!&A^$p1wsmw{`*QHSALdq5cG~eJ)BXv}61j})$ zJzn6+yFe?o$GXZ)F`TgA`(hZE($${ZYS$%dtG&f)$2P2X1-SU&=t8EeeMG8y#3@?? z{^>cWrR*GRaSo!C!0I$oIC(OhgK~tGk*Xl8TY)Qsma;1YBCJqeyrL9t+A6Ggq0fv; zeH3rIN;IGKdZnf8dbNQ!yNfC(@GCcp%k025#W zOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz286JP>NfC(@GCcp%k025#W MOyEBwkS?eEFHnk0ga7~l literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/BellHybrid/gui_image_update_failed.bin b/scripts/lua/assets/BellHybrid/gui_image_update_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..ab137291c63c527ab11f157829d6127fa6c4a1fe GIT binary patch literal 36002 zcmeI3F>dTO5Qb&Aa1&rtrU)Ldkt)1OP720Tjq;KHSkkTqPmUBeBH2x4YP zS(ievwgI(!(lKUYK_&}x;!(~`MbOIMlku!Q@?sx($YE44}~v*b;7F{=!jZ>kiQ zBsr>etjq2a!;9ec2#C&n^HqdC3%kOU=~A{r>P6(d3%kOUd?dFDDS6c7(X5a%B4@I~ zDK}NNa!R#yA*M84#%nwhoQ%q{M-}y`yh6vTcsVoFjvPnNN;zM1vQ>;eSL9iw!g8E9 z6{J12YunLv=o+`PRfMLa$*I6?M;|kNJh!t|gq}%ymZ`vPN6&9P_qVfEgj3$sE4R#X zDtwoeVJQX{F-?};%4Rw5mj3&mEz6tAW;ySY{-2{2H{?~cG@=X2JXuaRys2x6^O2J8 zdRB0}scT7!Wmv9(6c3IExl1Zd@3RRLqYpt({@9hfNhclM#m)C{`jpI7iE&AabmBc> znyf_vYjBdH(a{*W?XHTLr)kwd>NroUJyJdni-gj&3VKmZJ2SWQVt%q|S_Qq-XNsh- zS?c^HO{)e{{=2Iry}zolJG7FTI@3xjbheq)vHy%x?FQHod&9}laEdlOaax?t4HucT zAk&v%`bOQe^vzQ37UYIs6Aiz-+I*$P6`{!&eY%oqa{1BxqNvTKn&s8A$pvJ(ByMt9 zo1|{3GT(E3mN%8na2{U@#Zzdq>{d3X^3nlSUHMK!sX={ey2@2u`HsF~oY$QEbQ$>| zoiHz|sxRBoS3&dTR(7@ud{*ABhXeW{h4$2^Y&*IRUE_APiqLd4ITg6==wqgj=XSP= z&@)NTG8MS(==rVZ{&u#Cn5=Y-bc$^&Gn6BDWZ6bU=@P|`JX7M);PUcClsx`k^jDa2 zR_gg$EGEwteHIl<$#f}OEJR;gB1du)-MOYbYVv62=8L$=u8vvFDr?P`s#Q|!sN`6y zq*m6xDqkc$E%s_HU1dhYUx2QJR%&I%EYXvh&Ba)D6&r^+W35)rfEt3vThkMmY7XbsYtFZW_Ma5<05o&h9KIr8j&LsNw5Lq1{VU zO(;PG1V8`;KmY_l00ck)1pW;Ijz84VjpfG;VfCvYc9B|d1Y_4@u=E)v%{Y$cs?zfX ze&`#!hFU4gPWSZtrKpzl8;MURWvR5#rmEdizZts8b-~-ohUto``WmR~hF%7W(p+g~ zGsml`>zz9t?k6n9lg`PVRz)L&H9!(^$dKL=0h z6|9C?DlAgrGf0i+?%>?xTCQdf(Vt(T%V<_J5_E8<1iO{LTygHKYg zi_}nwoeo!$dS9d-qtqk&WsUQAfGqVA2UssP&-NtMKab%t&i04WUl~t_c*4=?%&h1f zq<=%~dKmk%;YF#*d`S786iA9zh9_U9M1S4Ii<5Qk=ANoLpZsC`V&Zyr=I3=KZ8E>7 zRrGj83$VZNhSpK;GXAneC&tpBoACooZ)ihnJ3Tq%zd*Zo^CMU4*xTG*fi)jeD~m1b zbF$j18v8*21V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l i00ck)1V8`;KmY_l00ck)1V8`;KmY{(j|BR5gZ>-jdXt|3 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/BellHybrid/gui_image_update_in_progress.bin b/scripts/lua/assets/BellHybrid/gui_image_update_in_progress.bin new file mode 100644 index 0000000000000000000000000000000000000000..a48762ac3756114ad209d67d65c7bcf26e24dc21 GIT binary patch literal 36002 zcmeI3y^iBH5XZGy;M@e~dw?J;(_WckalE%ln{q4DiHj}$2zrqeUKofumY?AE5Hh{0;~WlzzVPetN<&(3a|pK04u->{09}##Vk{=O;Hpg538P8{g4NR zm|Esw={Zo^ENa!%pVu6{S`nmC=`16N(e09WTDkC-Bnk!ZJ4C-vAa1+ZCV|m~Vk%&1 zCG8S6=0Xq&6bfxsPgwWH)cqh)FWOX+JzOGB7i*$XUE=h`nrI9P4XRBkI2{=Bg^Y377k z>E~;7ug*b4SPMS7UkQW5+WqLj!yS??=F}P6G(^`e7Fg36x zq7bU>&ybT)Yy4VWsO1ceF!!+~qLk><8sUt@mWWcK??zffx+kF|qLvE&(6ExW z(>4@~J=!P)NZd>;65q;re2hBmg0NAwYd(gLamdC4e} z$;=Q~!r#O~GsRuq%&=R#i5v(cRIL#3XBHHa@}ORkq`wN-rXHX}72gkvR^SyJ8LU~@ zt0S7z%78M31Vk2;&4^m&=+z5}Q|CYt$mbjT#-1(J&U_;4OPQzf7L(RcTKUI^+!w%6s>68;l zcOHu}H3Bv%r?oD22@v}2N=M;2`x5@rdQ~Ez7S_^IN~3@+Q%Z?JaINH?O(db#h{|&j zM>7Q(zAkd*&x4fsJ!kZLfEwYPYa9E2( zh3dG&jYRbBx;JQF1+~aZIQ%X(A`!iNt9lFfYrhg}hqJDX4G;XKUkfV!2cvA^c^T8{I(UzY} zVszAX$06RQUiZDzIvkaMtapO0e>`ZduRDxy9Wubri}CTEXT}GhgF=I9-Z#aV!?=l< zu&ciFuCMsimcQ7xkP_a1Yl`*(9z^RO79*2EKQDe3 zud8~u*_$852lWvyQ$GXhb=4I5cQcze^;p!KS*Wr)==oL~DK<*H*v0^=I@tB8U5M(W z$D&T=Nv+o>JKwggT0>Z6Pl$r1-Pr)@&_ zp%L|LD?l9`3YvCjyO4(?@J3E&xRoX_j!+<6fHG02Mzy(61-$nN&$)J$fdd?Rg-y+a zk>~LT*g|I7Tc{!mr6s<`_pMggbcVr;=v71i=5hmNzQo9WvVW<6ZuQC%O388efzS3Q zTb1AT3Y*Tcs3*I_)P{*;cQArOwyzNd*{;MJtU7nf>a{*wK;eYjYM6tN*&Pa+)+h4? z=b#cJ?oI>BP3OJEMMesPD+A5J_1+3XowRxxZh7DA8=z>t+O7cg2^@>p2RV|z6 z1+G^LR>iyJWxMZIQM>zl4y&law~z@{&uY~a=Bt2*+BIa}n9{H9@T*5_`)@8t3)nxx zJ3;1y(h}^5Qgyz^_c8IOAXT?0Py3-qjPT{{kQ%LDK*L literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/BellHybrid/gui_image_update_success.bin b/scripts/lua/assets/BellHybrid/gui_image_update_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..4b55741e1040374a92ef850746f21739d958f517 GIT binary patch literal 36002 zcmeI3y^h;B5XZG=AZ`w;D^ne>yEf%j;p1GJ2RN*}*uv#Lf?vgp6fW}$Ug!X|7F

  • ((<+!%p0L(Tj{{y1c8+W9R5c?kg_AOwVf5D)@FKnMr{As_^VfDjM@ zLO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTHrCnV5h>xFvVWm%?{(K@c+bhC_9|D%q* zUP7zMQKemk*0Jb2bl+c6Uk^s$RHW(vbp7~ngv#8KE{(S73&1)K9eGOXkV*bJNSxB8 zEGmShQ?)uZC`?AQVq1)~$Un_JK zu`nO!%!0Vb9$j~MKX@OnXR`=Shm&K0*Bw4*d_J#dvj|>EyvkVMb%)nCul?)UEMmvJ zu3&DMvt!{&Qi@Ay=7u@5+?gAsHOrm3AyQqROA1qkB=cCq*}YkV9e*6+87ce0#H1xl z>FIAzkTT_cQj-)f{;Ao=FpJK7h_lgrN%|;EF-D4f6ik)sR+Dt96x=K)wOK;SZkzjJ z)Z&}!*y?q+Yv!oOV7}FltzLI4A6$?l;zpf3T1*F<6PnIg|bak0B#-nv(uG5>(Oq=vw zjEK#dq&9_V*xBZo>m;?e*(p+b=)iEIrUp0WriY~O_CC5pYtU_$J9C4i6pJ=*m|07J zvtzN-EU;EjXF(}m-sWAdXD42S@%1}yUoXOZ8u+$3ZmK?+FJdazLO$)Vb}wha!vTIs z!9B0OxFCK=!Tac9#F)I`4}li^c@ix5qp5MAau)(ZKnMr{As_^VfDjM@KNA5x zc9xzKz1KEAo8P-ak4dmrYyLpdPEbDbz#V-s#5!;I#pDcy0@| z4Unoked+X}Dlvasqk<~&TvN56t7mLFcuT4C8BnUIs;bYsTwtfqI1jsCs_gEH0I9kJ zb!m&Tsxa@{rG`gc171<;d)a+1D*s-+BL&G)*eBboP^rr?!vSV1H$W6RVYxwu{DTJg zj#M5h1&AGhV8>p8!H$#X-zz8-=Dj6VhG0jTmr{D{)!9g$!Sj?<)o1KgH><0Q8DOYX zU+wi!?O38ywKt$q3ah<_o!KU@Tw0ZvxCZf$S%WBrtIw`MG)l7uYfx8I%wr?f>ax>Y z+!^|Xg9{ONhNeS?y%g>Y&7Jm>!u{&YKWn({_bVf1_p84?uVB43`&B#qR^p0YtN*}V zw9TR4MYCRN+(qx!kmdj4F1qge^FN;#9?-5Z4o@+=4@I9a?(NX;@wm7DJsg(y&%9r+ zf5Sh21K=emQeK!$(2vQ=zyA^`1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@ zLO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnVPg2^7nX{Xb+l BeW(Bc literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_backup_failed.bin b/scripts/lua/assets/PurePhone/gui_image_backup_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..f7887d2f4b266ff310b72f7d093dc2875a263d02 GIT binary patch literal 36002 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 XuZA3-wUYJmb7!r5HRSkzO{@O^0!;kl literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_backup_success.bin b/scripts/lua/assets/PurePhone/gui_image_backup_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..de04c8592e4e41d054a10b9bdc2301805cb0ed29 GIT binary patch literal 36002 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 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_factory_reset_failed.bin b/scripts/lua/assets/PurePhone/gui_image_factory_reset_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..e1f5d6a3b9cb6e58975210937719b873ec865875 GIT binary patch literal 36002 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 D1l5Y} literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_factory_reset_success.bin b/scripts/lua/assets/PurePhone/gui_image_factory_reset_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..33e70944acfa05f0fdc039cc959c1ba6750122bb GIT binary patch literal 36002 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 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_keys_failed.bin b/scripts/lua/assets/PurePhone/gui_image_keys_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..e592e02ee18a4ce2b39c084db1e433562a5d4eae GIT binary patch literal 36002 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 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_keys_in_progress.bin b/scripts/lua/assets/PurePhone/gui_image_keys_in_progress.bin new file mode 100644 index 0000000000000000000000000000000000000000..beb045d1eac22f213f88778b001ab61c3430bbae GIT binary patch literal 36002 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 TS|z24GKyA_B*}zdgI50sWL>dV literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_recovery_failed.bin b/scripts/lua/assets/PurePhone/gui_image_recovery_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..2bace194fd328acbc3ce3a8940caa2a16fe1eee5 GIT binary patch literal 36002 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 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_restore_failed.bin b/scripts/lua/assets/PurePhone/gui_image_restore_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..5be9bdb570fb704e749518f3d39a57cc18cc267a GIT binary patch literal 36002 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 Lts&?CZ?^g$0;v6k literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_restore_success.bin b/scripts/lua/assets/PurePhone/gui_image_restore_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..1511cd8aa9ae6d0b8b761913baeee7e57511098f GIT binary patch literal 36002 zcmeI3&2Hql5y!PKuzibj+{0p(yg`n07(lPGL0({iy#{Da3`}2w%nK~gkCLAC!IuE@ z2rFj}zIKp9Izgiq7ORR9TVhFnS%{4Z3An}2pU6Lp9}?wX7wvQ0g(FRjh{<%34|K#A#WpSPv_dwX)QS)3R2v9#$x8WvLUVWvya8 ztWegtThmR+c((TGlGo!wS8mR%Nnc zaIL<0R8p!I*6MYliXzpAa06FF?VnO=!L_RXwI1r98I@AtI)23-JltNxH4Ga=Q)*3;^z^?I;g zaX439_s2fi7~K>_ADfMS$ z^|JR@E27^2ZbXw*lwGU$Z&Kw(;#Wld)!KfvP_679{1c#6QzR{iO!NR4!-}Ypj?gHo zRg~K} zHDz}{df8EgFpTQ&MUQTlIEVEZMtykbYUKyqVJi`^R^{h_x@^dMD%}q`LP0%PbrbTb zB0}qd?)n^*bo@sTWE5oWy;|KdT?NCw zXQW5{BKSF%rqo=IR~|w=3RaV5UW2X5o{CT(wL51rBwJWpMKl=o@IgqIQtvE2LP3+v zZYPBER#7d}2?b3u;lZT0>txknRIv7cgi5tM!k44Cyyr0$)+*F0*n%5*9!RKVlZ^JH z18@((s4$#MUlspzspvu|Xp-4W!?-vhoGa;vP|zf!!}+qM;^OvnB^2bElC!fuQq=1P zqx8Nf_0ZKfno!U?871^Av}GG56!cC;QQgAhVtjy5(2EpB^^Co3eH9m@dd4p1PAKS| zjMnOtdg$smvmW)PA3f9G8#gj?7Nhq4TY7t4j4C5k#ga`FgAlRKem>0nBl zbaKM0C^jpA`;1DZiZX?m3^B zm7kWqF-#Tjr`NYWwC6ZIq@@`iMoD2o>6?K?D6WlPt(;fPYFPtgl$g~rlniHXpqkY( zlniG^saY*U$#7AU;mjyCtAJ|016VSg8Kq_wP={Z!+0Ck2gi^EWP+#+0 zkE7JA0_qMz@v_AYRI>`G-R^Eyg?y&|a1LhGq3(fYLWZ-SWP(}wof{439fFch`$Sdb zH+1CFWdcG`LFX8C!+{>^BcPQO_NX?fhU zQg{0z?_?f)t}t+Pp>O_J+Qs{+^7iA&$A#gnJDJnApLfC^p~57isENXHa)b&t`W)0} ze}gb`bOU)Vw(*^_3COs%9H7rFn1z-#X-&Y<1)Uc?%G@K96 zzMWB|vvX=#WE+IKZ+8?8IYAvOE~TiQJb5B4GH>V9Bp;mL&sPgjH0Yd@OL(+gQY+e< z_1p*NUs@}EWDclkf(HiOM9@bT;a}D&PyoBuC#cm9=cvC`f-1dwY&V@?d9q( zUO7TRWAOeHGpNsk^ZiLEXl8gv*65rNIpEEUP>}mZP;%k2C%V9>H$3UdJ9epspC@3H ze#I&i7PEYSQ1wROb1rXxwcD@dLxH* zTVAW4xu2*~+(1ODux5(Y6NGJyYE}Q1!ZTf1GkZ45R6-qoKNCaQe$v`-t9(grt4E2| z%>wEe^jmc)!975zW)?#-1=OX~V&WqdqRxiNOhA@=APj`S!^uf(^w?Mkya0>#IgAF zj7zB9SdmoRim22rxPf=+WF!T*t0bLJ(CeM1OtD~;90`r^2nGEcRQJvHHH}b^3!k12 zO2H^O#~Faw7)GMgQU5a(v{kdUf>3r3LMYp=Ae3$G?Fm_)lb!4Bss>P%i8>bP^g%(} zuIkO8KssUqwM~~Cq^yW4?S&p`U|p1hnsNlsS5+c)B>G^YTErMyvw@h2nkZ=C*tNn? zBl>X^$^KF(Jw6*&4qhho_-qA~9-pm%(&MugQ1;`}@CHVVo_P(kz_PE1(&MugYNf|# zE1>lFYz5R1p)s)RT5x!UQTB_J;SG!!`$dXTaF}GjNEzlgV(b?wM!{jynq$yhCWf~* zV%DtH=hS^*yRu)Tq;4}B|K-0(8ErO2!m?M%LtGfO2?tZx)q_!A{iV8QNEk-dWw{pW zSnbzBo!3LPUpmS@^Gfu{c=U>>@f%PqY@!*JR$zWgmOX0~6v)O9q?q(%ttKT38M0PE zib+q_YEq(*A!`++nDk_=CM60PvQ|NgNl(^lQlgL{YZauJ^kl6jB?=j`RzZqMPu6Nu iqL3kL6{MK-WUVG83K_CiL5fLF)@o9skm3JKtN#KoFs1VV literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_update_failed.bin b/scripts/lua/assets/PurePhone/gui_image_update_failed.bin new file mode 100644 index 0000000000000000000000000000000000000000..658d335ea9f7866035efe368854279874ef40653 GIT binary patch literal 36002 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 literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_update_in_progress.bin b/scripts/lua/assets/PurePhone/gui_image_update_in_progress.bin new file mode 100644 index 0000000000000000000000000000000000000000..8e929ff7b9802d66671d83892a7bce5cb62f3e1a GIT binary patch literal 36002 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 VIlgEm>*MDxTKQ_o@&B1t{{;uf18x8S literal 0 HcmV?d00001 diff --git a/scripts/lua/assets/PurePhone/gui_image_update_success.bin b/scripts/lua/assets/PurePhone/gui_image_update_success.bin new file mode 100644 index 0000000000000000000000000000000000000000..17b2adb5f5a9d8852948d6b9f19d5c5820f2f166 GIT binary patch literal 36002 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 0 and lfs.attributes(b, 'mode') ~= 'directory' then + local r, m = helpers.mkdirp(b) + if not r then + return r, m + end + end + return lfs.mkdir(p) +end + +return helpers diff --git a/scripts/lua/share/ltar.lua b/scripts/lua/share/ltar.lua new file mode 100644 index 0000000000000000000000000000000000000000..d9c55e4d869564b9f4dd4835719de18e32bdb3c8 --- /dev/null +++ b/scripts/lua/share/ltar.lua @@ -0,0 +1,233 @@ +--- LUA support for TAR packages +-- @module ltar +local microtar = require("lmicrotar") +local lfs = require("lfs") + +local tar = {} + +local function strip_from_prefix(prefix, filename) + local name = filename:gsub(prefix, "") + name = name:sub(2, -1) + return name +end + +local function get_filename(file) + return file:match("^.+/(.+)$") +end + +local function basedir(p) + return p:gsub('[^\\/]+[\\/]?$', '') +end + +local function mkdirp(p) + if lfs.attributes(p, 'mode') == 'directory' then + return nil, 'already exists' + end + + local b = basedir(p) + if #b > 0 and lfs.attributes(b, 'mode') ~= 'directory' then + local r, m = mkdirp(b) + if not r then + return r, m + end + end + return lfs.mkdir(p) +end + +local function dirtree(dir) + assert(dir and dir ~= "", "directory parameter is missing or empty") + if string.sub(dir, -1) == "/" then + dir = string.sub(dir, 1, -2) + end + + local function yieldtree(dir) + for entry in lfs.dir(dir) do + if entry ~= "." and entry ~= ".." then + entry = dir .. "/" .. entry + local attr = lfs.attributes(entry) + coroutine.yield(entry, attr) + if attr.mode == "directory" then + yieldtree(entry) + end + end + end + end + + return coroutine.wrap(function() + yieldtree(dir) + end) +end + +local function write_tarfile_chunks(handle, fd) + local size = 1024 * 512 + while true do + local block = fd:read(size) + if not block then + break + end + handle:write_data(block, block:len()) + end +end + +local function read_tarfile_chunks(handle, fd, total_size) + local block_size = 1024 * 512 + local to_read = {}; + + while total_size > 0 do + if total_size > block_size then + to_read = block_size + else + to_read = total_size + end + fd:write(handle:read_data(to_read)) + total_size = total_size - to_read + end +end + +--- Create empty tar file +-- @function create +-- @param path where to put newly created tar file +-- @return tar handle or nil +function tar.create(path) + Handle = { + handle = microtar.open(path, "w"), + add_file = function(self, filename) + local size = lfs.attributes(filename, "size") + local fd = io.open(filename, "r") + self.handle:write_file_header(filename, size) + write_tarfile_chunks(self.handle, fd) + fd:close() + end, + add_directory = function(self, path) + self.handle:write_dir_header(path) + end, + close = function(self) + self.handle:close() + end + } + return Handle +end + +--- Iterate over contents of tar file +-- @function iter_by_handle +-- @param handle tar handle create by @{create} +function tar.iter_by_handle(handle) + return function() + local f = handle:read_header() + if f then + handle:next() + return f + end + end +end + +--- Iterate over contents of tar file +-- @function iter_by_path +-- @param path path to tar file +function tar.iter_by_path(path) + local handle = microtar.open(path) + return function() + local f = handle:read_header() + if f then + handle:next() + return f + end + end +end + +--- Pack contents of specified dir to tar file +-- @function create_from_path +-- @param path directory +-- @param where where to save tar file +function tar.create_from_path(path, where) + tar.create_from_path_match(path, where, ".*") +end + +--- Pack contents of specified dir to tar file using regex +-- @function create_from_path_regex +-- @param path directory +-- @param where where to save tar file +-- @param matcher regex expression +function tar.create_from_path_regex(path, where, matcher) + local handle = microtar.open(where, "w") + for filename, attr in dirtree(path) do + local name = strip_from_prefix(path, filename) + if name:match(matcher) then + if attr.mode == "directory" then + handle:write_dir_header(name) + else + handle:write_file_header(name, attr.size) + local fd = io.open(filename, "r") + write_tarfile_chunks(handle, fd) + fd:close() + end + end + end + handle:close() +end + +--- Unpack tar file to specified directory +-- @function unpack +-- @param path directory +-- @param where where to store unpacked files +function tar.unpack(path, where) + local handle = microtar.open(path) + local header = handle:read_header() + while header do + if header.type == microtar.TDIR then + mkdirp(where .. "/" .. header.name) + elseif header.type == microtar.TREG then + local fd = io.open(where .. "/" .. header.name, "w") + read_tarfile_chunks(handle, fd, header.size) + fd:close() + end + handle:next() + header = handle:read_header() + end + handle:close() +end + +--- Append directory to already existing tar file +-- @function append +-- @param path directory +-- @param where location of already existing tar file +function tar.append(path, where) + local handle = microtar.open(where, "a") + for filename, attr in dirtree(path) do + if attr.mode == "directory" then + handle:write_dir_header(filename) + else + handle:write_file_header(filename, attr.size) + local fd = io.open(filename, "r") + write_tarfile_chunks(handle, fd) + fd:close() + end + end + handle:close() +end + +--- Append file to already existing tar file +-- @function append_file +-- @param path file path +-- @param where location of already existing tar file +function tar.append_file(path, where) + local filename = get_filename(path) + local size = lfs.attributes(path, 'size') + local handle = microtar.open(where, "a") + handle:write_file_header(filename, size) + local fd = io.open(path, "r") + write_tarfile_chunks(handle, fd) + fd:close() + handle:close() +end + +function tar.find(tar_path, what) + local handle = microtar.open(tar_path) + local stats = handle:find(what) + if stats ~= nil then + return handle:read_data() + end + return nil +end + +return tar diff --git a/scripts/lua/share/lunajson.lua b/scripts/lua/share/lunajson.lua new file mode 100644 index 0000000000000000000000000000000000000000..9b9bc477fcb81d66fbb9f04252f9423b87828efb --- /dev/null +++ b/scripts/lua/share/lunajson.lua @@ -0,0 +1,11 @@ +local newdecoder = require 'lunajson.decoder' +local newencoder = require 'lunajson.encoder' +local sax = require 'lunajson.sax' +-- If you need multiple contexts of decoder and/or encoder, +-- you can require lunajson.decoder and/or lunajson.encoder directly. +return { + decode = newdecoder(), + encode = newencoder(), + newparser = sax.newparser, + newfileparser = sax.newfileparser, +} diff --git a/scripts/lua/share/lunajson/decoder.lua b/scripts/lua/share/lunajson/decoder.lua new file mode 100644 index 0000000000000000000000000000000000000000..98a0babc11b72339975e44520274638e54c5ff2a --- /dev/null +++ b/scripts/lua/share/lunajson/decoder.lua @@ -0,0 +1,515 @@ +local setmetatable, tonumber, tostring = + setmetatable, tonumber, tostring +local floor, inf = + math.floor, math.huge +local mininteger, tointeger = + math.mininteger or nil, math.tointeger or nil +local byte, char, find, gsub, match, sub = + string.byte, string.char, string.find, string.gsub, string.match, string.sub + +local function _decode_error(pos, errmsg) + error("parse error at " .. pos .. ": " .. errmsg, 2) +end + +local f_str_ctrl_pat +if _VERSION == "Lua 5.1" then + -- use the cluttered pattern because lua 5.1 does not handle \0 in a pattern correctly + f_str_ctrl_pat = '[^\32-\255]' +else + f_str_ctrl_pat = '[\0-\31]' +end + +local _ENV = nil + + +local function newdecoder() + local json, pos, nullv, arraylen, rec_depth + + -- `f` is the temporary for dispatcher[c] and + -- the dummy for the first return value of `find` + local dispatcher, f + + --[[ + Helper + --]] + local function decode_error(errmsg) + return _decode_error(pos, errmsg) + end + + --[[ + Invalid + --]] + local function f_err() + decode_error('invalid value') + end + + --[[ + Constants + --]] + -- null + local function f_nul() + if sub(json, pos, pos+2) == 'ull' then + pos = pos+3 + return nullv + end + decode_error('invalid value') + end + + -- false + local function f_fls() + if sub(json, pos, pos+3) == 'alse' then + pos = pos+4 + return false + end + decode_error('invalid value') + end + + -- true + local function f_tru() + if sub(json, pos, pos+2) == 'rue' then + pos = pos+3 + return true + end + decode_error('invalid value') + end + + --[[ + Numbers + Conceptually, the longest prefix that matches to `[-+.0-9A-Za-z]+` (in regexp) + is captured as a number and its conformance to the JSON spec is checked. + --]] + -- deal with non-standard locales + local radixmark = match(tostring(0.5), '[^0-9]') + local fixedtonumber = tonumber + if radixmark ~= '.' then + if find(radixmark, '%W') then + radixmark = '%' .. radixmark + end + fixedtonumber = function(s) + return tonumber(gsub(s, '.', radixmark)) + end + end + + local function number_error() + return decode_error('invalid number') + end + + -- `0(\.[0-9]*)?([eE][+-]?[0-9]*)?` + local function f_zro(mns) + local num, c = match(json, '^(%.?[0-9]*)([-+.A-Za-z]?)', pos) -- skipping 0 + + if num == '' then + if c == '' then + if mns then + return -0.0 + end + return 0 + end + + if c == 'e' or c == 'E' then + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + if c == '' then + pos = pos + #num + if mns then + return -0.0 + end + return 0.0 + end + end + number_error() + end + + if byte(num) ~= 0x2E or byte(num, -1) == 0x2E then + number_error() + end + + if c ~= '' then + if c == 'e' or c == 'E' then + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + end + if c ~= '' then + number_error() + end + end + + pos = pos + #num + c = fixedtonumber(num) + + if mns then + c = -c + end + return c + end + + -- `[1-9][0-9]*(\.[0-9]*)?([eE][+-]?[0-9]*)?` + local function f_num(mns) + pos = pos-1 + local num, c = match(json, '^([0-9]+%.?[0-9]*)([-+.A-Za-z]?)', pos) + if byte(num, -1) == 0x2E then -- error if ended with period + number_error() + end + + if c ~= '' then + if c ~= 'e' and c ~= 'E' then + number_error() + end + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + if not num or c ~= '' then + number_error() + end + end + + pos = pos + #num + c = fixedtonumber(num) + + if mns then + c = -c + if c == mininteger and not find(num, '[^0-9]') then + c = mininteger + end + end + return c + end + + -- skip minus sign + local function f_mns() + local c = byte(json, pos) + if c then + pos = pos+1 + if c > 0x30 then + if c < 0x3A then + return f_num(true) + end + else + if c > 0x2F then + return f_zro(true) + end + end + end + decode_error('invalid number') + end + + --[[ + Strings + --]] + local f_str_hextbl = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, inf, inf, inf, inf, inf, inf, + inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, + __index = function() + return inf + end + } + setmetatable(f_str_hextbl, f_str_hextbl) + + local f_str_escapetbl = { + ['"'] = '"', + ['\\'] = '\\', + ['/'] = '/', + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + __index = function() + decode_error("invalid escape sequence") + end + } + setmetatable(f_str_escapetbl, f_str_escapetbl) + + local function surrogate_first_error() + return decode_error("1st surrogate pair byte not continued by 2nd") + end + + local f_str_surrogate_prev = 0 + local function f_str_subst(ch, ucode) + if ch == 'u' then + local c1, c2, c3, c4, rest = byte(ucode, 1, 5) + ucode = f_str_hextbl[c1-47] * 0x1000 + + f_str_hextbl[c2-47] * 0x100 + + f_str_hextbl[c3-47] * 0x10 + + f_str_hextbl[c4-47] + if ucode ~= inf then + if ucode < 0x80 then -- 1byte + if rest then + return char(ucode, rest) + end + return char(ucode) + elseif ucode < 0x800 then -- 2bytes + c1 = floor(ucode / 0x40) + c2 = ucode - c1 * 0x40 + c1 = c1 + 0xC0 + c2 = c2 + 0x80 + if rest then + return char(c1, c2, rest) + end + return char(c1, c2) + elseif ucode < 0xD800 or 0xE000 <= ucode then -- 3bytes + c1 = floor(ucode / 0x1000) + ucode = ucode - c1 * 0x1000 + c2 = floor(ucode / 0x40) + c3 = ucode - c2 * 0x40 + c1 = c1 + 0xE0 + c2 = c2 + 0x80 + c3 = c3 + 0x80 + if rest then + return char(c1, c2, c3, rest) + end + return char(c1, c2, c3) + elseif 0xD800 <= ucode and ucode < 0xDC00 then -- surrogate pair 1st + if f_str_surrogate_prev == 0 then + f_str_surrogate_prev = ucode + if not rest then + return '' + end + surrogate_first_error() + end + f_str_surrogate_prev = 0 + surrogate_first_error() + else -- surrogate pair 2nd + if f_str_surrogate_prev ~= 0 then + ucode = 0x10000 + + (f_str_surrogate_prev - 0xD800) * 0x400 + + (ucode - 0xDC00) + f_str_surrogate_prev = 0 + c1 = floor(ucode / 0x40000) + ucode = ucode - c1 * 0x40000 + c2 = floor(ucode / 0x1000) + ucode = ucode - c2 * 0x1000 + c3 = floor(ucode / 0x40) + c4 = ucode - c3 * 0x40 + c1 = c1 + 0xF0 + c2 = c2 + 0x80 + c3 = c3 + 0x80 + c4 = c4 + 0x80 + if rest then + return char(c1, c2, c3, c4, rest) + end + return char(c1, c2, c3, c4) + end + decode_error("2nd surrogate pair byte appeared without 1st") + end + end + decode_error("invalid unicode codepoint literal") + end + if f_str_surrogate_prev ~= 0 then + f_str_surrogate_prev = 0 + surrogate_first_error() + end + return f_str_escapetbl[ch] .. ucode + end + + -- caching interpreted keys for speed + local f_str_keycache = setmetatable({}, {__mode="v"}) + + local function f_str(iskey) + local newpos = pos + local tmppos, c1, c2 + repeat + newpos = find(json, '"', newpos, true) -- search '"' + if not newpos then + decode_error("unterminated string") + end + tmppos = newpos-1 + newpos = newpos+1 + c1, c2 = byte(json, tmppos-1, tmppos) + if c2 == 0x5C and c1 == 0x5C then -- skip preceding '\\'s + repeat + tmppos = tmppos-2 + c1, c2 = byte(json, tmppos-1, tmppos) + until c2 ~= 0x5C or c1 ~= 0x5C + tmppos = newpos-2 + end + until c2 ~= 0x5C -- leave if '"' is not preceded by '\' + + local str = sub(json, pos, tmppos) + pos = newpos + + if iskey then -- check key cache + tmppos = f_str_keycache[str] -- reuse tmppos for cache key/val + if tmppos then + return tmppos + end + tmppos = str + end + + if find(str, f_str_ctrl_pat) then + decode_error("unescaped control string") + end + if find(str, '\\', 1, true) then -- check whether a backslash exists + -- We need to grab 4 characters after the escape char, + -- for encoding unicode codepoint to UTF-8. + -- As we need to ensure that every first surrogate pair byte is + -- immediately followed by second one, we grab upto 5 characters and + -- check the last for this purpose. + str = gsub(str, '\\(.)([^\\]?[^\\]?[^\\]?[^\\]?[^\\]?)', f_str_subst) + if f_str_surrogate_prev ~= 0 then + f_str_surrogate_prev = 0 + decode_error("1st surrogate pair byte not continued by 2nd") + end + end + if iskey then -- commit key cache + f_str_keycache[tmppos] = str + end + return str + end + + --[[ + Arrays, Objects + --]] + -- array + local function f_ary() + rec_depth = rec_depth + 1 + if rec_depth > 1000 then + decode_error('too deeply nested json (> 1000)') + end + local ary = {} + + pos = match(json, '^[ \n\r\t]*()', pos) + + local i = 0 + if byte(json, pos) == 0x5D then -- check closing bracket ']' which means the array empty + pos = pos+1 + else + local newpos = pos + repeat + i = i+1 + f = dispatcher[byte(json,newpos)] -- parse value + pos = newpos+1 + ary[i] = f() + newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos) -- check comma + until not newpos + + newpos = match(json, '^[ \n\r\t]*%]()', pos) -- check closing bracket + if not newpos then + decode_error("no closing bracket of an array") + end + pos = newpos + end + + if arraylen then -- commit the length of the array if `arraylen` is set + ary[0] = i + end + rec_depth = rec_depth - 1 + return ary + end + + -- objects + local function f_obj() + rec_depth = rec_depth + 1 + if rec_depth > 1000 then + decode_error('too deeply nested json (> 1000)') + end + local obj = {} + + pos = match(json, '^[ \n\r\t]*()', pos) + if byte(json, pos) == 0x7D then -- check closing bracket '}' which means the object empty + pos = pos+1 + else + local newpos = pos + + repeat + if byte(json, newpos) ~= 0x22 then -- check '"' + decode_error("not key") + end + pos = newpos+1 + local key = f_str(true) -- parse key + + -- optimized for compact json + -- c1, c2 == ':', or + -- c1, c2, c3 == ':', ' ', + f = f_err + local c1, c2, c3 = byte(json, pos, pos+3) + if c1 == 0x3A then + if c2 ~= 0x20 then + f = dispatcher[c2] + newpos = pos+2 + else + f = dispatcher[c3] + newpos = pos+3 + end + end + if f == f_err then -- read a colon and arbitrary number of spaces + newpos = match(json, '^[ \n\r\t]*:[ \n\r\t]*()', pos) + if not newpos then + decode_error("no colon after a key") + end + f = dispatcher[byte(json, newpos)] + newpos = newpos+1 + end + pos = newpos + obj[key] = f() -- parse value + newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos) + until not newpos + + newpos = match(json, '^[ \n\r\t]*}()', pos) + if not newpos then + decode_error("no closing bracket of an object") + end + pos = newpos + end + + rec_depth = rec_depth - 1 + return obj + end + + --[[ + The jump table to dispatch a parser for a value, + indexed by the code of the value's first char. + Nil key means the end of json. + --]] + dispatcher = { [0] = + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_str, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_mns, f_err, f_err, + f_zro, f_num, f_num, f_num, f_num, f_num, f_num, f_num, + f_num, f_num, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_ary, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_fls, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_nul, f_err, + f_err, f_err, f_err, f_err, f_tru, f_err, f_err, f_err, + f_err, f_err, f_err, f_obj, f_err, f_err, f_err, f_err, + __index = function() + decode_error("unexpected termination") + end + } + setmetatable(dispatcher, dispatcher) + + --[[ + run decoder + --]] + local function decode(json_, pos_, nullv_, arraylen_) + json, pos, nullv, arraylen = json_, pos_, nullv_, arraylen_ + rec_depth = 0 + + pos = match(json, '^[ \n\r\t]*()', pos) + + f = dispatcher[byte(json, pos)] + pos = pos+1 + local v = f() + + if pos_ then + return v, pos + else + f, pos = find(json, '^[ \n\r\t]*', pos) + if pos ~= #json then + decode_error('json ended') + end + return v + end + end + + return decode +end + +return newdecoder diff --git a/scripts/lua/share/lunajson/encoder.lua b/scripts/lua/share/lunajson/encoder.lua new file mode 100644 index 0000000000000000000000000000000000000000..606767a3e0aed8ac90d82077674940f844aaa83b --- /dev/null +++ b/scripts/lua/share/lunajson/encoder.lua @@ -0,0 +1,185 @@ +local error = error +local byte, find, format, gsub, match = string.byte, string.find, string.format, string.gsub, string.match +local concat = table.concat +local tostring = tostring +local pairs, type = pairs, type +local setmetatable = setmetatable +local huge, tiny = 1/0, -1/0 + +local f_string_esc_pat +if _VERSION == "Lua 5.1" then + -- use the cluttered pattern because lua 5.1 does not handle \0 in a pattern correctly + f_string_esc_pat = '[^ -!#-[%]^-\255]' +else + f_string_esc_pat = '[\0-\31"\\]' +end + +local _ENV = nil + + +local function newencoder() + local v, nullv + local i, builder, visited + + local function f_tostring(v) + builder[i] = tostring(v) + i = i+1 + end + + local radixmark = match(tostring(0.5), '[^0-9]') + local delimmark = match(tostring(12345.12345), '[^0-9' .. radixmark .. ']') + if radixmark == '.' then + radixmark = nil + end + + local radixordelim + if radixmark or delimmark then + radixordelim = true + if radixmark and find(radixmark, '%W') then + radixmark = '%' .. radixmark + end + if delimmark and find(delimmark, '%W') then + delimmark = '%' .. delimmark + end + end + + local f_number = function(n) + if tiny < n and n < huge then + local s = format("%.17g", n) + if radixordelim then + if delimmark then + s = gsub(s, delimmark, '') + end + if radixmark then + s = gsub(s, radixmark, '.') + end + end + builder[i] = s + i = i+1 + return + end + error('invalid number') + end + + local doencode + + local f_string_subst = { + ['"'] = '\\"', + ['\\'] = '\\\\', + ['\b'] = '\\b', + ['\f'] = '\\f', + ['\n'] = '\\n', + ['\r'] = '\\r', + ['\t'] = '\\t', + __index = function(_, c) + return format('\\u00%02X', byte(c)) + end + } + setmetatable(f_string_subst, f_string_subst) + + local function f_string(s) + builder[i] = '"' + if find(s, f_string_esc_pat) then + s = gsub(s, f_string_esc_pat, f_string_subst) + end + builder[i+1] = s + builder[i+2] = '"' + i = i+3 + end + + local function f_table(o) + if visited[o] then + error("loop detected") + end + visited[o] = true + + local tmp = o[0] + if type(tmp) == 'number' then -- arraylen available + builder[i] = '[' + i = i+1 + for j = 1, tmp do + doencode(o[j]) + builder[i] = ',' + i = i+1 + end + if tmp > 0 then + i = i-1 + end + builder[i] = ']' + + else + tmp = o[1] + if tmp ~= nil then -- detected as array + builder[i] = '[' + i = i+1 + local j = 2 + repeat + doencode(tmp) + tmp = o[j] + if tmp == nil then + break + end + j = j+1 + builder[i] = ',' + i = i+1 + until false + builder[i] = ']' + + else -- detected as object + builder[i] = '{' + i = i+1 + local tmp = i + for k, v in pairs(o) do + if type(k) ~= 'string' then + error("non-string key") + end + f_string(k) + builder[i] = ':' + i = i+1 + doencode(v) + builder[i] = ',' + i = i+1 + end + if i > tmp then + i = i-1 + end + builder[i] = '}' + end + end + + i = i+1 + visited[o] = nil + end + + local dispatcher = { + boolean = f_tostring, + number = f_number, + string = f_string, + table = f_table, + __index = function() + error("invalid type value") + end + } + setmetatable(dispatcher, dispatcher) + + function doencode(v) + if v == nullv then + builder[i] = 'null' + i = i+1 + return + end + return dispatcher[type(v)](v) + end + + local function encode(v_, nullv_) + v, nullv = v_, nullv_ + i, builder, visited = 1, {}, {} + + doencode(v) + return concat(builder) + end + + return encode +end + +return newencoder diff --git a/scripts/lua/share/lunajson/sax.lua b/scripts/lua/share/lunajson/sax.lua new file mode 100644 index 0000000000000000000000000000000000000000..19bb1158a86bd41d425e93583a9a99699844fb28 --- /dev/null +++ b/scripts/lua/share/lunajson/sax.lua @@ -0,0 +1,719 @@ +local setmetatable, tonumber, tostring = + setmetatable, tonumber, tostring +local floor, inf = + math.floor, math.huge +local mininteger, tointeger = + math.mininteger or nil, math.tointeger or nil +local byte, char, find, gsub, match, sub = + string.byte, string.char, string.find, string.gsub, string.match, string.sub + +local function _parse_error(pos, errmsg) + error("parse error at " .. pos .. ": " .. errmsg, 2) +end + +local f_str_ctrl_pat +if _VERSION == "Lua 5.1" then + -- use the cluttered pattern because lua 5.1 does not handle \0 in a pattern correctly + f_str_ctrl_pat = '[^\32-\255]' +else + f_str_ctrl_pat = '[\0-\31]' +end + +local type, unpack = type, table.unpack or unpack +local open = io.open + +local _ENV = nil + + +local function nop() end + +local function newparser(src, saxtbl) + local json, jsonnxt, rec_depth + local jsonlen, pos, acc = 0, 1, 0 + + -- `f` is the temporary for dispatcher[c] and + -- the dummy for the first return value of `find` + local dispatcher, f + + -- initialize + if type(src) == 'string' then + json = src + jsonlen = #json + jsonnxt = function() + json = '' + jsonlen = 0 + jsonnxt = nop + end + else + jsonnxt = function() + acc = acc + jsonlen + pos = 1 + repeat + json = src() + if not json then + json = '' + jsonlen = 0 + jsonnxt = nop + return + end + jsonlen = #json + until jsonlen > 0 + end + jsonnxt() + end + + local sax_startobject = saxtbl.startobject or nop + local sax_key = saxtbl.key or nop + local sax_endobject = saxtbl.endobject or nop + local sax_startarray = saxtbl.startarray or nop + local sax_endarray = saxtbl.endarray or nop + local sax_string = saxtbl.string or nop + local sax_number = saxtbl.number or nop + local sax_boolean = saxtbl.boolean or nop + local sax_null = saxtbl.null or nop + + --[[ + Helper + --]] + local function tryc() + local c = byte(json, pos) + if not c then + jsonnxt() + c = byte(json, pos) + end + return c + end + + local function parse_error(errmsg) + return _parse_error(acc + pos, errmsg) + end + + local function tellc() + return tryc() or parse_error("unexpected termination") + end + + local function spaces() -- skip spaces and prepare the next char + while true do + pos = match(json, '^[ \n\r\t]*()', pos) + if pos <= jsonlen then + return + end + if jsonlen == 0 then + parse_error("unexpected termination") + end + jsonnxt() + end + end + + --[[ + Invalid + --]] + local function f_err() + parse_error('invalid value') + end + + --[[ + Constants + --]] + -- fallback slow constants parser + local function generic_constant(target, targetlen, ret, sax_f) + for i = 1, targetlen do + local c = tellc() + if byte(target, i) ~= c then + parse_error("invalid char") + end + pos = pos+1 + end + return sax_f(ret) + end + + -- null + local function f_nul() + if sub(json, pos, pos+2) == 'ull' then + pos = pos+3 + return sax_null(nil) + end + return generic_constant('ull', 3, nil, sax_null) + end + + -- false + local function f_fls() + if sub(json, pos, pos+3) == 'alse' then + pos = pos+4 + return sax_boolean(false) + end + return generic_constant('alse', 4, false, sax_boolean) + end + + -- true + local function f_tru() + if sub(json, pos, pos+2) == 'rue' then + pos = pos+3 + return sax_boolean(true) + end + return generic_constant('rue', 3, true, sax_boolean) + end + + --[[ + Numbers + Conceptually, the longest prefix that matches to `[-+.0-9A-Za-z]+` (in regexp) + is captured as a number and its conformance to the JSON spec is checked. + --]] + -- deal with non-standard locales + local radixmark = match(tostring(0.5), '[^0-9]') + local fixedtonumber = tonumber + if radixmark ~= '.' then + if find(radixmark, '%W') then + radixmark = '%' .. radixmark + end + fixedtonumber = function(s) + return tonumber(gsub(s, '.', radixmark)) + end + end + + local function number_error() + return parse_error('invalid number') + end + + -- fallback slow parser + local function generic_number(mns) + local buf = {} + local i = 1 + local is_int = true + + local c = byte(json, pos) + pos = pos+1 + + local function nxt() + buf[i] = c + i = i+1 + c = tryc() + pos = pos+1 + end + + if c == 0x30 then + nxt() + if c and 0x30 <= c and c < 0x3A then + number_error() + end + else + repeat nxt() until not (c and 0x30 <= c and c < 0x3A) + end + if c == 0x2E then + is_int = false + nxt() + if not (c and 0x30 <= c and c < 0x3A) then + number_error() + end + repeat nxt() until not (c and 0x30 <= c and c < 0x3A) + end + if c == 0x45 or c == 0x65 then + is_int = false + nxt() + if c == 0x2B or c == 0x2D then + nxt() + end + if not (c and 0x30 <= c and c < 0x3A) then + number_error() + end + repeat nxt() until not (c and 0x30 <= c and c < 0x3A) + end + if c and (0x41 <= c and c <= 0x5B or + 0x61 <= c and c <= 0x7B or + c == 0x2B or c == 0x2D or c == 0x2E) then + number_error() + end + pos = pos-1 + + local num = char(unpack(buf)) + num = fixedtonumber(num) + if mns then + num = -num + if num == mininteger and is_int then + num = mininteger + end + end + return sax_number(num) + end + + -- `0(\.[0-9]*)?([eE][+-]?[0-9]*)?` + local function f_zro(mns) + local num, c = match(json, '^(%.?[0-9]*)([-+.A-Za-z]?)', pos) -- skipping 0 + + if num == '' then + if pos > jsonlen then + pos = pos - 1 + return generic_number(mns) + end + if c == '' then + if mns then + return sax_number(-0.0) + end + return sax_number(0) + end + + if c == 'e' or c == 'E' then + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + if c == '' then + pos = pos + #num + if pos > jsonlen then + pos = pos - #num - 1 + return generic_number(mns) + end + if mns then + return sax_number(-0.0) + end + return sax_number(0.0) + end + end + pos = pos-1 + return generic_number(mns) + end + + if byte(num) ~= 0x2E or byte(num, -1) == 0x2E then + pos = pos-1 + return generic_number(mns) + end + + if c ~= '' then + if c == 'e' or c == 'E' then + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + end + if c ~= '' then + pos = pos-1 + return generic_number(mns) + end + end + + pos = pos + #num + if pos > jsonlen then + pos = pos - #num - 1 + return generic_number(mns) + end + c = fixedtonumber(num) + + if mns then + c = -c + end + return sax_number(c) + end + + -- `[1-9][0-9]*(\.[0-9]*)?([eE][+-]?[0-9]*)?` + local function f_num(mns) + pos = pos-1 + local num, c = match(json, '^([0-9]+%.?[0-9]*)([-+.A-Za-z]?)', pos) + if byte(num, -1) == 0x2E then -- error if ended with period + return generic_number(mns) + end + + if c ~= '' then + if c ~= 'e' and c ~= 'E' then + return generic_number(mns) + end + num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos) + if not num or c ~= '' then + return generic_number(mns) + end + end + + pos = pos + #num + if pos > jsonlen then + pos = pos - #num + return generic_number(mns) + end + c = fixedtonumber(num) + + if mns then + c = -c + if c == mininteger and not find(num, '[^0-9]') then + c = mininteger + end + end + return sax_number(c) + end + + -- skip minus sign + local function f_mns() + local c = byte(json, pos) or tellc() + if c then + pos = pos+1 + if c > 0x30 then + if c < 0x3A then + return f_num(true) + end + else + if c > 0x2F then + return f_zro(true) + end + end + end + parse_error("invalid number") + end + + --[[ + Strings + --]] + local f_str_hextbl = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, inf, inf, inf, inf, inf, inf, + inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, inf, inf, inf, inf, inf, inf, inf, + inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, + __index = function() + return inf + end + } + setmetatable(f_str_hextbl, f_str_hextbl) + + local f_str_escapetbl = { + ['"'] = '"', + ['\\'] = '\\', + ['/'] = '/', + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + __index = function() + parse_error("invalid escape sequence") + end + } + setmetatable(f_str_escapetbl, f_str_escapetbl) + + local function surrogate_first_error() + return parse_error("1st surrogate pair byte not continued by 2nd") + end + + local f_str_surrogate_prev = 0 + local function f_str_subst(ch, ucode) + if ch == 'u' then + local c1, c2, c3, c4, rest = byte(ucode, 1, 5) + ucode = f_str_hextbl[c1-47] * 0x1000 + + f_str_hextbl[c2-47] * 0x100 + + f_str_hextbl[c3-47] * 0x10 + + f_str_hextbl[c4-47] + if ucode ~= inf then + if ucode < 0x80 then -- 1byte + if rest then + return char(ucode, rest) + end + return char(ucode) + elseif ucode < 0x800 then -- 2bytes + c1 = floor(ucode / 0x40) + c2 = ucode - c1 * 0x40 + c1 = c1 + 0xC0 + c2 = c2 + 0x80 + if rest then + return char(c1, c2, rest) + end + return char(c1, c2) + elseif ucode < 0xD800 or 0xE000 <= ucode then -- 3bytes + c1 = floor(ucode / 0x1000) + ucode = ucode - c1 * 0x1000 + c2 = floor(ucode / 0x40) + c3 = ucode - c2 * 0x40 + c1 = c1 + 0xE0 + c2 = c2 + 0x80 + c3 = c3 + 0x80 + if rest then + return char(c1, c2, c3, rest) + end + return char(c1, c2, c3) + elseif 0xD800 <= ucode and ucode < 0xDC00 then -- surrogate pair 1st + if f_str_surrogate_prev == 0 then + f_str_surrogate_prev = ucode + if not rest then + return '' + end + surrogate_first_error() + end + f_str_surrogate_prev = 0 + surrogate_first_error() + else -- surrogate pair 2nd + if f_str_surrogate_prev ~= 0 then + ucode = 0x10000 + + (f_str_surrogate_prev - 0xD800) * 0x400 + + (ucode - 0xDC00) + f_str_surrogate_prev = 0 + c1 = floor(ucode / 0x40000) + ucode = ucode - c1 * 0x40000 + c2 = floor(ucode / 0x1000) + ucode = ucode - c2 * 0x1000 + c3 = floor(ucode / 0x40) + c4 = ucode - c3 * 0x40 + c1 = c1 + 0xF0 + c2 = c2 + 0x80 + c3 = c3 + 0x80 + c4 = c4 + 0x80 + if rest then + return char(c1, c2, c3, c4, rest) + end + return char(c1, c2, c3, c4) + end + parse_error("2nd surrogate pair byte appeared without 1st") + end + end + parse_error("invalid unicode codepoint literal") + end + if f_str_surrogate_prev ~= 0 then + f_str_surrogate_prev = 0 + surrogate_first_error() + end + return f_str_escapetbl[ch] .. ucode + end + + local function f_str(iskey) + local pos2 = pos + local newpos + local str = '' + local bs + while true do + while true do -- search '\' or '"' + newpos = find(json, '[\\"]', pos2) + if newpos then + break + end + str = str .. sub(json, pos, jsonlen) + if pos2 == jsonlen+2 then + pos2 = 2 + else + pos2 = 1 + end + jsonnxt() + if jsonlen == 0 then + parse_error("unterminated string") + end + end + if byte(json, newpos) == 0x22 then -- break if '"' + break + end + pos2 = newpos+2 -- skip '\' + bs = true -- mark the existence of a backslash + end + str = str .. sub(json, pos, newpos-1) + pos = newpos+1 + + if find(str, f_str_ctrl_pat) then + parse_error("unescaped control string") + end + if bs then -- a backslash exists + -- We need to grab 4 characters after the escape char, + -- for encoding unicode codepoint to UTF-8. + -- As we need to ensure that every first surrogate pair byte is + -- immediately followed by second one, we grab upto 5 characters and + -- check the last for this purpose. + str = gsub(str, '\\(.)([^\\]?[^\\]?[^\\]?[^\\]?[^\\]?)', f_str_subst) + if f_str_surrogate_prev ~= 0 then + f_str_surrogate_prev = 0 + parse_error("1st surrogate pair byte not continued by 2nd") + end + end + + if iskey then + return sax_key(str) + end + return sax_string(str) + end + + --[[ + Arrays, Objects + --]] + -- arrays + local function f_ary() + rec_depth = rec_depth + 1 + if rec_depth > 1000 then + parse_error('too deeply nested json (> 1000)') + end + sax_startarray() + + spaces() + if byte(json, pos) == 0x5D then -- check closing bracket ']' which means the array empty + pos = pos+1 + else + local newpos + while true do + f = dispatcher[byte(json, pos)] -- parse value + pos = pos+1 + f() + newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos) -- check comma + if newpos then + pos = newpos + else + newpos = match(json, '^[ \n\r\t]*%]()', pos) -- check closing bracket + if newpos then + pos = newpos + break + end + spaces() -- since the current chunk can be ended, skip spaces toward following chunks + local c = byte(json, pos) + pos = pos+1 + if c == 0x2C then -- check comma again + spaces() + elseif c == 0x5D then -- check closing bracket again + break + else + parse_error("no closing bracket of an array") + end + end + if pos > jsonlen then + spaces() + end + end + end + + rec_depth = rec_depth - 1 + return sax_endarray() + end + + -- objects + local function f_obj() + rec_depth = rec_depth + 1 + if rec_depth > 1000 then + parse_error('too deeply nested json (> 1000)') + end + sax_startobject() + + spaces() + if byte(json, pos) == 0x7D then -- check closing bracket '}' which means the object empty + pos = pos+1 + else + local newpos + while true do + if byte(json, pos) ~= 0x22 then + parse_error("not key") + end + pos = pos+1 + f_str(true) -- parse key + newpos = match(json, '^[ \n\r\t]*:[ \n\r\t]*()', pos) -- check colon + if newpos then + pos = newpos + else + spaces() -- read spaces through chunks + if byte(json, pos) ~= 0x3A then -- check colon again + parse_error("no colon after a key") + end + pos = pos+1 + spaces() + end + if pos > jsonlen then + spaces() + end + f = dispatcher[byte(json, pos)] + pos = pos+1 + f() -- parse value + newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos) -- check comma + if newpos then + pos = newpos + else + newpos = match(json, '^[ \n\r\t]*}()', pos) -- check closing bracket + if newpos then + pos = newpos + break + end + spaces() -- read spaces through chunks + local c = byte(json, pos) + pos = pos+1 + if c == 0x2C then -- check comma again + spaces() + elseif c == 0x7D then -- check closing bracket again + break + else + parse_error("no closing bracket of an object") + end + end + if pos > jsonlen then + spaces() + end + end + end + + rec_depth = rec_depth - 1 + return sax_endobject() + end + + --[[ + The jump table to dispatch a parser for a value, + indexed by the code of the value's first char. + Key should be non-nil. + --]] + dispatcher = { [0] = + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_str, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_mns, f_err, f_err, + f_zro, f_num, f_num, f_num, f_num, f_num, f_num, f_num, + f_num, f_num, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_ary, f_err, f_err, f_err, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_fls, f_err, + f_err, f_err, f_err, f_err, f_err, f_err, f_nul, f_err, + f_err, f_err, f_err, f_err, f_tru, f_err, f_err, f_err, + f_err, f_err, f_err, f_obj, f_err, f_err, f_err, f_err, + } + + --[[ + public funcitons + --]] + local function run() + rec_depth = 0 + spaces() + f = dispatcher[byte(json, pos)] + pos = pos+1 + f() + end + + local function read(n) + if n < 0 then + error("the argument must be non-negative") + end + local pos2 = (pos-1) + n + local str = sub(json, pos, pos2) + while pos2 > jsonlen and jsonlen ~= 0 do + jsonnxt() + pos2 = pos2 - (jsonlen - (pos-1)) + str = str .. sub(json, pos, pos2) + end + if jsonlen ~= 0 then + pos = pos2+1 + end + return str + end + + local function tellpos() + return acc + pos + end + + return { + run = run, + tryc = tryc, + read = read, + tellpos = tellpos, + } +end + +local function newfileparser(fn, saxtbl) + local fp = open(fn) + local function gen() + local s + if fp then + s = fp:read(8192) + if not s then + fp:close() + fp = nil + end + end + return s + end + return newparser(gen, saxtbl) +end + +return { + newparser = newparser, + newfileparser = newfileparser +} diff --git a/scripts/lua/update.lua b/scripts/lua/update.lua new file mode 100644 index 0000000000000000000000000000000000000000..e3336bfedc39b115523ccd8fad01329af4981f2c --- /dev/null +++ b/scripts/lua/update.lua @@ -0,0 +1,11 @@ +local update = {} + +update.script_name = "update.lua" +update.img_in_progress = "assets/gui_image_update_in_progress.bin" +update.img_success = "assets/gui_image_update_success.bin" +update.img_failure = "assets/gui_image_update_failed.bin" + +function update.execute() +end + +return update \ No newline at end of file