From 79bcd5a1c6b780752a9511e9c70fd5d259c5f80f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:44:45 +0300 Subject: [PATCH] Jetpack joyride and NFC Maker apps --- ReadMe.md | 2 + .../external/jetpack_joyride/application.fam | 15 + .../jetpack_joyride/assets/air_vent.png | Bin 0 -> 1838 bytes .../jetpack_joyride/assets/alert/frame_01.png | Bin 0 -> 1820 bytes .../jetpack_joyride/assets/alert/frame_02.png | Bin 0 -> 1807 bytes .../jetpack_joyride/assets/alert/frame_rate | 1 + .../jetpack_joyride/assets/barry/frame_01.png | Bin 0 -> 1932 bytes .../jetpack_joyride/assets/barry/frame_02.png | Bin 0 -> 1930 bytes .../jetpack_joyride/assets/barry/frame_03.png | Bin 0 -> 1929 bytes .../jetpack_joyride/assets/barry/frame_rate | 1 + .../jetpack_joyride/assets/barry_infill.png | Bin 0 -> 1614 bytes .../external/jetpack_joyride/assets/bg1.png | Bin 0 -> 835 bytes .../external/jetpack_joyride/assets/bg2.png | Bin 0 -> 968 bytes .../external/jetpack_joyride/assets/bg3.png | Bin 0 -> 886 bytes .../external/jetpack_joyride/assets/coin.png | Bin 0 -> 1842 bytes .../jetpack_joyride/assets/coin_infill.png | Bin 0 -> 1974 bytes .../jetpack_joyride/assets/dead_scientist.png | Bin 0 -> 1768 bytes .../assets/dead_scientist_infill.png | Bin 0 -> 1900 bytes .../external/jetpack_joyride/assets/door.png | Bin 0 -> 1961 bytes .../assets/missile/frame_01.png | Bin 0 -> 1898 bytes .../jetpack_joyride/assets/missile/frame_rate | 1 + .../jetpack_joyride/assets/missile_infill.png | Bin 0 -> 1591 bytes .../jetpack_joyride/assets/pillar.png | Bin 0 -> 1880 bytes .../jetpack_joyride/assets/scientist_left.png | Bin 0 -> 2056 bytes .../assets/scientist_left_infill.png | Bin 0 -> 2211 bytes .../assets/scientist_right.png | Bin 0 -> 1917 bytes .../assets/scientist_right_infill.png | Bin 0 -> 2210 bytes .../external/jetpack_joyride/icon.png | Bin 0 -> 1836 bytes .../includes/background_asset.c | 81 ++++ .../includes/background_assets.h | 34 ++ .../external/jetpack_joyride/includes/barry.c | 33 ++ .../external/jetpack_joyride/includes/barry.h | 23 ++ .../external/jetpack_joyride/includes/coin.c | 98 +++++ .../external/jetpack_joyride/includes/coin.h | 26 ++ .../jetpack_joyride/includes/game_sprites.h | 35 ++ .../jetpack_joyride/includes/game_state.c | 5 + .../jetpack_joyride/includes/game_state.h | 34 ++ .../jetpack_joyride/includes/missile.c | 86 ++++ .../jetpack_joyride/includes/missile.h | 24 ++ .../jetpack_joyride/includes/particle.c | 57 +++ .../jetpack_joyride/includes/particle.h | 21 + .../external/jetpack_joyride/includes/point.h | 14 + .../jetpack_joyride/includes/scientist.c | 77 ++++ .../jetpack_joyride/includes/scientist.h | 29 ++ .../jetpack_joyride/includes/states.h | 9 + .../external/jetpack_joyride/jetpack.c | 379 ++++++++++++++++++ .../external/nfc_maker/application.fam | 14 + applications/external/nfc_maker/nfc_maker.c | 81 ++++ applications/external/nfc_maker/nfc_maker.h | 59 +++ .../external/nfc_maker/nfc_maker_10px.png | Bin 0 -> 4142 bytes .../nfc_maker/scenes/nfc_maker_scene.c | 30 ++ .../nfc_maker/scenes/nfc_maker_scene.h | 29 ++ .../scenes/nfc_maker_scene_bluetooth.c | 57 +++ .../nfc_maker/scenes/nfc_maker_scene_config.h | 13 + .../nfc_maker/scenes/nfc_maker_scene_https.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_mail.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_menu.c | 61 +++ .../nfc_maker/scenes/nfc_maker_scene_name.c | 57 +++ .../nfc_maker/scenes/nfc_maker_scene_phone.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_result.c | 359 +++++++++++++++++ .../nfc_maker/scenes/nfc_maker_scene_text.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_url.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_wifi.c | 55 +++ .../scenes/nfc_maker_scene_wifi_auth.c | 83 ++++ .../scenes/nfc_maker_scene_wifi_encr.c | 48 +++ .../scenes/nfc_maker_scene_wifi_pass.c | 53 +++ 66 files changed, 2349 insertions(+) create mode 100644 applications/external/jetpack_joyride/application.fam create mode 100644 applications/external/jetpack_joyride/assets/air_vent.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_02.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_02.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_03.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/barry_infill.png create mode 100644 applications/external/jetpack_joyride/assets/bg1.png create mode 100644 applications/external/jetpack_joyride/assets/bg2.png create mode 100644 applications/external/jetpack_joyride/assets/bg3.png create mode 100644 applications/external/jetpack_joyride/assets/coin.png create mode 100644 applications/external/jetpack_joyride/assets/coin_infill.png create mode 100644 applications/external/jetpack_joyride/assets/dead_scientist.png create mode 100644 applications/external/jetpack_joyride/assets/dead_scientist_infill.png create mode 100644 applications/external/jetpack_joyride/assets/door.png create mode 100644 applications/external/jetpack_joyride/assets/missile/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/missile/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/missile_infill.png create mode 100644 applications/external/jetpack_joyride/assets/pillar.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_left.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_left_infill.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_right.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_right_infill.png create mode 100644 applications/external/jetpack_joyride/icon.png create mode 100644 applications/external/jetpack_joyride/includes/background_asset.c create mode 100644 applications/external/jetpack_joyride/includes/background_assets.h create mode 100644 applications/external/jetpack_joyride/includes/barry.c create mode 100644 applications/external/jetpack_joyride/includes/barry.h create mode 100644 applications/external/jetpack_joyride/includes/coin.c create mode 100644 applications/external/jetpack_joyride/includes/coin.h create mode 100644 applications/external/jetpack_joyride/includes/game_sprites.h create mode 100644 applications/external/jetpack_joyride/includes/game_state.c create mode 100644 applications/external/jetpack_joyride/includes/game_state.h create mode 100644 applications/external/jetpack_joyride/includes/missile.c create mode 100644 applications/external/jetpack_joyride/includes/missile.h create mode 100644 applications/external/jetpack_joyride/includes/particle.c create mode 100644 applications/external/jetpack_joyride/includes/particle.h create mode 100644 applications/external/jetpack_joyride/includes/point.h create mode 100644 applications/external/jetpack_joyride/includes/scientist.c create mode 100644 applications/external/jetpack_joyride/includes/scientist.h create mode 100644 applications/external/jetpack_joyride/includes/states.h create mode 100644 applications/external/jetpack_joyride/jetpack.c create mode 100644 applications/external/nfc_maker/application.fam create mode 100644 applications/external/nfc_maker/nfc_maker.c create mode 100644 applications/external/nfc_maker/nfc_maker.h create mode 100644 applications/external/nfc_maker/nfc_maker_10px.png create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.h create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_config.h create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_https.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_name.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_result.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_text.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_url.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c diff --git a/ReadMe.md b/ReadMe.md index b589e164e..f57256713 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -170,6 +170,7 @@ You can support us by using links or addresses below: - IR Scope [(by kallanreed)](https://github.com/DarkFlippers/unleashed-firmware/pull/407) - **BadBT** plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) (See in Applications->Tools) - (aka BadUSB via Bluetooth) - **Mifare Nested** [(by AloneLiberty)](https://github.com/AloneLiberty/FlipperNested) - Works with PC and python app `FlipperNested` +- **NFC Maker** plugin (make tags with URLs, Wifi and other things) [(by Willy-JL)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/external/nfc_maker) Games: - DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) @@ -185,6 +186,7 @@ Games: - BlackJack [(by teeebor)](https://github.com/teeebor/flipper_games) - 2048 game [(by eugene-kirzhanov)](https://github.com/eugene-kirzhanov/flipper-zero-2048-game) - Bomberduck [(by leo-need-more-coffee)](https://github.com/leo-need-more-coffee/flipperzero-bomberduck) +- JetPack Joyride [(by timstrasser)](https://github.com/timstrasser) # Instructions diff --git a/applications/external/jetpack_joyride/application.fam b/applications/external/jetpack_joyride/application.fam new file mode 100644 index 000000000..1b98e11ce --- /dev/null +++ b/applications/external/jetpack_joyride/application.fam @@ -0,0 +1,15 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="jetpack_joyride", + name="Jetpack Joyride", + apptype=FlipperAppType.EXTERNAL, + entry_point="jetpack_game_app", + cdefines=["APP_JETPACK_GAME"], + requires=["gui"], + stack_size=4 * 1024, + order=100, + fap_icon="icon.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/jetpack_joyride/assets/air_vent.png b/applications/external/jetpack_joyride/assets/air_vent.png new file mode 100644 index 0000000000000000000000000000000000000000..a7fcf0b203a79875b27f4a90870bf66b8ec4e597 GIT binary patch literal 1838 zcma)7eNYr-7+>=vJkTK&D$80|6O6gry@NZr?G^^^PVR(b2#y2?!R79~yN$cuZFi45 z&{CWMzs4aHwWh|=88TCYMy9E3nlw#?$Q&RiDQiNQoW`Q2X0kz}clYo(B%S`)yM3SE z@A-V6=h+=4#RUsgOH>GgEU?=wrSLxiKJjr6!QXR9<8cTwH<@u(NEMDE(!~Wdw4191 zns9)JID(jR!#wS(0}@&bybMcWV;_Htp^Tft*6JOEgEs>oV`~&ZS!1!&)mY~;y0P3G zl_^X@0|6k>XgJ_!MKVlbid_=E%VHcu6^c|xVHJ)N)XWJ0)ob(`0?SdMCc*6?OD(G> zmEnoPe3HbII35axG@%R)CwOtK(P+d89j?=Md> z23=x2_o2(k3_7f}!|~uO0)c6Xh?2D)7Gkof(*nAix9Du(0bUA3E-1KwwH~n2a-|l~ z8)+5*EddLBC`?D_GSq}YO=LQ?*(6~g_4;%|M-Wl6gL5;UhW{jIky-;Jld){r9&TEq z{|`S}a8hPRf`YC2z=)=F%$%PSU~n*p3?9puJ!%(d1O$N(nzF@5hYG&P(+bADg!S? zPLZRZQ3eP_l{O-2m+YUyc!6^VUBIoB0Gf-SBIl7pv;gwFu!ATp&*NcWqzx#XO~%Ut z0o9?frf~45*$nvrw0O@&#&iol867T<_1*AH_>2Th3L$0>K)r|z?iaYI@aU$3hv=>Y zEbJlyZn7=y*P9Sz))of#!;C&akhHd7+ZToTd5gE@K24>aJ(QxZvN?|@q`x{eXVvq0 zme=ySc30Ol4UJYm(-FA*LfZDBKi3V`lze+An7G)Q7^qu6cx0r$Q?+K~@k?)VuVnm0 zi1SVyZr^j|muj2$cg77@v0x*Xzb|YR;KpecAZR7%{x7$ zxmi5OceL#5*q^fhz`)+((e~b#`wnE+zTVuh0BIF043jZSAqOWsOgK(ABspe6;G?kK0-vJ$vg$ z^O2=#DGArdzae&}ZrIpzyhy+KPTcAoEAD*%pqXdSd>$;{b@R-qL89KF)m3}^!&kyj ze*e{>z~&xx^-%! literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_01.png b/applications/external/jetpack_joyride/assets/alert/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..ac4cca1b1f85f1ac36afa559e601804c50074838 GIT binary patch literal 1820 zcma)7eM}o=7(Y-phI~v%Hc{iwBam&--nEP_Jz5I1eALmV<3q}#lcRmx9?-kn-IbQ= z;+Vpexf#s9m`W=8hcg5WI?b4ah$cqI2Gg*qTa1pGOcsNgK@*3;_pVUN>K6Za+rIDb z_x_%b_j#VaVzZW~Bt4RZAV`Y2!c+yn6W~ftjEA3D@jnIOejQz77wwix(!qL_l#^`) z%7B-HID+U50giGs0}*WmZko|!vu8#zly>T|x_k>^;f%mTR|I)b9kkXsg3S)C6EhSh z=>jAa@B)!S1Kt)!AOm_#)+OO@=@`dQnIbmpF}uZv8d)Bo`O17HffXj9I^O9bt4w8! zm*Gy2c|?&Taoq3sEB$#&mUrVStyYT@YFw>WK!!qSV?-*TV1!IbBCKHof`g|yk!BfG z(xe($pQy($h@(rMdAW!P_^MD5^4p%7&JetgTSdkSx>|L%z;FyabKrhFz!iC zkwzX+A~3n;quH=LoRmoY zAATg^q{I$81xxdu9xdN7vMnqRor5vt$<8iAW~0%@vo5*?VnS7U32H7iYE)W{Mxj=! zWUlmXq>T=M7Q2b|0!Dx}rpNN{!0Mwgm3kSJO;^Ltg8E@S>0wE;mvVzT+9`ULq?V5> z0ppg=7iDBXdYu-ZRNl1=4c{|tL5A$hm>$z=@F*+wF_6?Ss7@{;vVs;wmT!}f!d+Ai zc87Q2Z3`{84~oh~L{bjPz8>Rv*6DKqryL}d3!?(-68#hpiruh&^jNXWMMF>9P}rHY zn*ltkMqx%_+po~{djM3t;~ArB1z+?ImzsLXJslnsfKDbvO#yf>ds}<+8!SqTl$zhrZExa?(xSpkfAp&pSybc404nd`j6=%@@=qWbvKCg>9of!g1|= zM{Q49Mk7$MgDc%v`_v^xom=o9(Oeemp(Cb8h0Ff*<-+ z&$M^0|9MU6)X>Lxo?df zGNqI^+09Kf)u!F^YmN2f)i0knKbQL40^uVb^2hM8|78Ix{vHx*kX~4)s|_ z&Kx$J8@|5fTJ0@Qe>ZAATD(K3(r2rb!Ae)xr{6D(?wOj-?B(ZL@$YsePrtMAgg;~_ zUY&J(zNvTbT*iQUYJM*B;oIqFZBu`zem0V@;c~5FX!WrxlYhM1d5SX+b=6>$E73u#FpD{7M6IbM`l+2nbuHr;Y!^LaSsl=N1xj6#^4{)8m^*VK#V`e}7&46BmEU=$>0gKdwfQ{apHAAAQxUf^5WG LYBdd%H0=8q6;5LQ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_02.png b/applications/external/jetpack_joyride/assets/alert/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..c3955f090f5c9e496e536ae34cb206384191a0bb GIT binary patch literal 1807 zcma)7ZA=?w94{km40+MXUQiP+M_A^dy=yzV^ypEb<)x02ju$D5PLB3zd(eB?-Ic!R z;@AQz8#9=_$1RBa;0ytSnP%W1qRA4612t^w7NcVyCX2z$;0K4n=dQez)h&Lw_MYeW zdw&0y=l}oT+m5R8w3KI3R4P@Pt-@Lj|C8X8n!E=7=B)W80@qK|wNA-tuOwU?qoLef z6VQYh9^xvM$sFP-R|}AkCg7!65}h3$MG@LfqV@T9+|FBokFJOape9mP>x#6vjBeCi zm|_YMP=Emvg@hPCD-t0RRdfmXE^lKfqEMt35_Q@gh=mgXlCR0v;AmkAViMdQqS{)v zco?oo)F(+ifnmX5P!rT^IKhi)jYcDe>oA>84H;@N%t};9&5GHwL{!5HM3+GG63wxQ ztVuO-0f|IWh$Bm$G5oSPD=tP4^MHjY9@A=YESAjsIEfQ|+#`-G!*K^eh+c_dl^Gd{ zqf4~s5p)@eqr+U=?T?RwVO9kqN~NtZ5sOJ(<xXm0Un; zghc?91g!8OQ5~++t8s%G&#%>K3EV*JFy`Vq9A75eIXCSI|0j7Tp~oSah-Jg_a8nZX zfB4G@CuDZiDOj41^=Rdch4XU)bPmR#SDameY!-_{;5@V+Vq$f931Ta?7_>%%L9Nqh z6|Q`5go6$Nztc)HfE8hlk!b!ySVIh^)vbVv=^EHsP(P|CUo2r`C@-j|-I8xfYGu0; zuwHq8QAPpe+wlkF@~&fPc%M-V3gleDNYrS+Vyra8L2|<&CZ&vs3R;vnA*^ggyQl{2 zi>{(eKdrP6f+$5qP%ha%iSh#H4!D3@2@=XhQIYdVK}rC{URXaQTI})A(9!Q!lw^!r}rjDTJ6Q0MAAD;Br1Bw$G#tH>*{O1-_L*cZfbe^MEkn@OFe&FzEJ-4yhrFAH*aluzb7N_?ATFj zT6we6)=X1jTeX@B)1rv6U}en|#_ezp!9V+|bxmlyvB9hN}2{;>Imq(^us0n60$R+F#Om@LxZYToM2P literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_rate b/applications/external/jetpack_joyride/assets/alert/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/alert/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/barry/frame_01.png b/applications/external/jetpack_joyride/assets/barry/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..8abdcaf6150ec448cba1279fd790c03985e4778e GIT binary patch literal 1932 zcma)7eM}Q~7(O>=80r*1rgJg59Y&_6z4rP+dbA+4d=xoWp=u#+di1XCNqcv@yULX@ z+{|!nGcr(v5dIKMoI|6iac*u7KjN4l(@X_JTy(l6nl0)aDC(H*cUL|IxBYSL{odz! zzn**Vy^mbEnG<7Y#X=A?(V6AQ1OMZ}H7Vvf@OyYglOKX!a4>F<IxHR;fVem*%>?rNv$gg`_0M zT4fvr2+Q(UOu9ZHYn434T;>u~<+YhGLioFf?L>l}K5`it$Q_XbcA}dIg4;7>htl}~Cvjqc;|D4l<}(0~aFiI|Gx8|N z2s)5&^b9h|k47elf2W5+!`cv~^wmI!K2e9Y(4RnbM|c{|qeU((cGP{UJ@({6VOoR0|t zOw7wngPrMiv)*DhYcOpzhoU3y43U01hi3g!03?sb9|V>$lxiR1pfnf^D-*JWWq>4@ znpbu43}QAU5krS~6^sgjhlUeix+zj39i#**#dMfKqcdqRy&J=D%zzt>3v?K+)2Ufh zK1VUW$bYewiH5C!PY`$(BTAeQQBBb?&!^vxo}$Mfqt3T7;R>JhD&5%-Uf`&(m!{Oz z>ijFH$oZraQlL})-~ro^RG*IlIz(Xb>M(wm7GMmlvwbUp-b-*#iA?S|k%)bcjoO3SXTQ#>% zOzJ7S-E&1e)U&)L6I<1><8G?C_R{gjtyogxo-g0x&*#2+U~E%!b#r1Z_ zecD-7TV6`1Z!WvaJ)U#AX6LD0d)J+vGVlDacPkGe?Ng_0Y)YUg&;CbG4(`biwzhf> z>toLPFO=OGcgZ~Mi=3|Czc-F=`&nLldbwrI!^z`P99QQT-+n%D<-M+*`(D0W@d}&c=wpaE;=gffOX7#% z6ZRsXRUm?|-c;op1JyUo1jdz4N2+_~=CCXEr6cf!DHoosshh3a@Z;{!3Ma?FUj?BD z8|O5{wfK52oVj_uzKcJC7RJik9+cOfc`fPaBiB**mAaa18HVE~$KSVpncrUDEN#0r myL(Gp`ofF%$nM_V-B6P&|8eVyd6Sfns53p+u{&+$+J6AnqoRNS literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/barry/frame_02.png b/applications/external/jetpack_joyride/assets/barry/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4587ac8f56f2c9ee981d4811def57ed952329e GIT binary patch literal 1930 zcma)7du$VR96uDv81fhoAv4CyjZgvGYj0g=Z|&yR?ors)F$(Jlg16qU?QQMdd3R;k zNe~yth7w^sGC=--B@$kd0U`!P5Tlca5RsJ(2#7I4FagE}lSih%yY9(``p32R`+h#( z*XO?Xy;Vi-{LzWYi3oy>b`>~_;r}RjjZJtC{vKW27(kF0oUF$yd#OUw$A=9x!kl#xYc*$Q5?XOBJCGUIb{GA$kLoY=+nO5|il%j@+$8ZA4C$fUf@*Jwh zq|5k-Y{xK&qus%Q!uCbyr0({i5V(rqCW8_0PZxr`%u7LD=&fjgPcJ;kQD*(m$fF{I z=up0)Gbpq$6q%y_ogNMkXhV{7mp~!9MIF#WPXfsk6#!lgBt9bgK<*O2$tju=sxG91 z5Aot$I2yo?nflB{tO7z_CmRNw1Vx9~(_m?TFirF96)hf>;u#n;^(n~Za1@EWpAA7w zD$dVAUAYd6$!f9a2}3-Gsw3$N(*aNjxPTml$>Z_+fD)F`?EM_n215~bLgsNSl!Vao zYA)VB%#NiKct5YwAtCtCND59jL(8<2mSLrYkx11W)AWSNLl7j9P7-M|jRa{lYFRWs z&#?aJf3ekxMs1K!QDgxt$-EfVOz|N;>QrH&9^$?DxdbL-PtigHWPmhUr8gv-g~$(a)#+!V8~XUtwR>0!%np88|@ zSjzRJro#y{cO`y2uW3u{;E8+UmDV#KE;zF0{<rPuhbsi)WNIkk8H%CqCApZ~qB?g-X0Vf^|=9m9AJKKlFczC3Z;G4D}R z!r8ztOYe-hXqos$Vf&vynnyMNs#Kp|Xnp_TxDi>-YcnctKOelhynWAsmoC?QMBP*_ zG*)$#h;^6OyOfNY9oFE{4<6N&otyP|tL;tZ?Qd=+KX|Y`|7<$mg|Np1H%@IzuZ5qm zkwQVK1iyL{)N2^BW$P4ZT;y_Qd!}rRterFFxG^E;)3&Y>|EsURl6|9LV~Ke=Q#$Of z&h&Ob{9%5kA-h_<;j1!+qx{#+L$X=JEoesvS9^&X=LZPN1rrDZe42Z$kpB3 i)Uiwd`P3bMxW`_&Pj#6`)hz-_=Nw4ky_x+yV zefPWfoJDz);%CQ05H!h=Z!ZSF6TtOc+*t5^bXl_xg2vfdms@s|g@lI>>M5G9VDw5* z05}AhvlM~yR5CJL!T4Ctf^>a-9D!Ndf|RC_7%A8oKbv1IG9}eTE>Cr($3!Dpnek?Y z011MOOuh0)e7cRaN?`6g@BcP=m>2LNOf0aUJl`NnuW=6dfleswtv5?2P0QSwUub4pwtg z6?{mxAPB(WzT}L;4rb@1zVQJOsEVKlJ%$c-7yP`;OMYG$=xA8X06Zd4X1!0Tqaq{R z0ez!aP$*$EXOjGP_h4|?7?PZ`0ua$h>aY>|3rMc8z@Wv9#D_!=le2>1l#eN7IG^kX#iRKLfl`*%>_Y<70RtgbA&WQ`P=af9 zwGj6pWzS`K%=KVpuapWnRL(89hah0anwus&Lhx@~G2U5JBYWkcXkQ(whEN zRN}pI6(urOAGpC5#On33fQK*)9v#-lF(Qn^xE|A|4v(txGazICbOxgfHxQVCNW=Q3 zLr3jLQ{7YhiK73jAMjN6_Y;j8-p@$3`}#4X>K5py%Z!d7H0m&9ZGbtVHn5JsUPA{K z8#oNCMur3XPy~yw{FXpM(3sUM5bFq$LeMMqq54b7_Jz|=XQg=7KdQ?vbAOY%$D6w2 zOkh*}x`x!;`v1@mF+E;cvQrWg^ zW#_WWx}=FS0-=+(T`f0iPp?f{+}(64<799-F?(j_w0k#2>-CHU8)n>Z`^{Bs2B>i3R3L7uv zmr3BQH%-09Kuud`0pn7K-RhdPIpnlXK7qx}|M2l+JmZHiV?Irq5rgFGlcCIpwJBp4 z|8=bCg{4bEvCr0g-m+@usfSnS$sOY()^TfSx?tU$3lrO?-0gX(bZ6vFLHf_VYhLU= fEO*iJ`OTS~pE-LAw;pO#-=L11BKsHFZ>;_YytSIB literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/barry/frame_rate b/applications/external/jetpack_joyride/assets/barry/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/barry/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/barry_infill.png b/applications/external/jetpack_joyride/assets/barry_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..9462801f02c43ffc7bc9fc70ab9b9ea902a908e4 GIT binary patch literal 1614 zcmcIlJ#W)M7mTfsOwFD?`=to}JGo6cz?MxqF`X>$&${zuekfo1U7P5(Hto zv|iYT-y87UoPd`wvpV@+5UzbAdPUmLu2a1R=K+`IdcvnYfQ@#51Iy zU%pBraSUlUW2&Z~$91ya4)9KUvuw8y?5ra#El%Yk9UQorSz_ciyikt}DfX+wf4;3q zVoYHNhEy@P#5@hKn2|HGDlJZlxxlIF+lAGRF{})!&X}(&O0(INn`xN_HATy2vxu+z72sbJP;$klZ(E+;*?hS&O*s>>D z;?<-MI89%IO8NX&K&zwySh&5mB9@Bzc`Z9XpF(mHgX^f5T&spRuvcSs2%h-w1G~hD z_XiAkfsI4nA&)%*lAt(lY*OiChO~f`0js^S5FDCrLU%hBvkDf2Op%JxDK(Qq3uRT) zRivlq9>H5x<0vtoI;7hA8=H5um;=6P>gyzAG-$9IzoRSE~ecXo+xjNX^56h(si-^4zdhvyQ=Iw>oXU zjG#JA&!jumDgQSgxRqDsgS0Y2I!fzvUA4#O6W@YA>XV@Vq|ZP;iPf+^qo?1=C#UeK z)IGkOGIGEPVPK4buE`#ZN0_rpZ_vV`H>$A*^ErS){ObPaA27tP`|Eol%>CQ^nGoK- zy9b+Msjyt0d}IH*cs_A_{!l%8a&r3d4os(eD;=bcIWPc58vBW{uwA0Hw&Lu Ho*w@ODWKxk literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg1.png b/applications/external/jetpack_joyride/assets/bg1.png new file mode 100644 index 0000000000000000000000000000000000000000..82d614e1cc45e9a74c6d44d4ae45b772807aae8d GIT binary patch literal 835 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zL7AQ|jv*Cu-p)BViAj-%b-M2V|IMv0h^{JmuI#%rSa=m@{%nKHon~S}#Y*?=Ru=#TBivFikZ4_Zx%pk#fK!btGLf((FCW)LCj5w*^b@qH$g|R*3$>ZJA zpMTGrwIO`binj-UOzldv?mo@$XZ&-@1WBtft$*(x xShl0Tw<`T`xX?Esf3NW-AFq#6TnHEY&z`#_Q`kX&5-=thJYD@<);T3K0RWSjY2^R_ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg2.png b/applications/external/jetpack_joyride/assets/bg2.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8590d3ada26094c39a3019b72084423c75ec41 GIT binary patch literal 968 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zLGL_W978JRyq$BguSJ2!HF5L*|EJ#^PT^STk-f0G^UmiK9)qp!+I?*?UY;z+`K=l* zFJv$fVQ6DI5Wp}a-h1(!;u4uWoy`{Uf0`%W<=ojf^D6TS`)v{~fy z!Q9RB_XYBvb1(l>u6$l)iS@G=TsLffPu;z_)b5}4R%1@Vi?6;noZUP>NNXvNUenAU z%6+jery~U-q9+S^IbGfmwOJO_DO zEzBW0?p0a&^Nk5k9_x2n5%>BjQ(JEzy~+5G3!s-tD=)+hD+FX>9_pJov#eO^bb ztK~C9SA`apSFC#ExB8|IE0a_*E$7%bnb?*JlzA|Ls0@Y+se_JJX-n z)~kM~J+XY-6|Um#g+Ht%?bsVQ>-hrw|6gGErvB~I_9x$;{F=OoRY3F}^MOB%JgZEz TaygEGawvnRtDnm{r-UW|@zu8~ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg3.png b/applications/external/jetpack_joyride/assets/bg3.png new file mode 100644 index 0000000000000000000000000000000000000000..ebb1dd66ba4e92493aacc09d90e44096d5035693 GIT binary patch literal 886 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zL32D^978JRyq$9}?~s82>+Dtk|DS%xc+%-eB>Vd5mIlQxKPpdNImmZaZtYh2hd-8b z|0ok-Sj-^7dO(BW+52;|`yPAv3BTDo>0x$|1|kfk%CpEu|~|2b{%-)Kk1G3C{h=b)tTch2l(m*?rUELq0F~>*cz6 z?r+ZMU#zT$jn6i%b5s4zx|gSBabj@WuD-~^3bp+!o_&`xJ6gKNcc*#1RgEr?KXqDa hP}l7MgmZqf2kd!Rba~T-7GUHtc)I$ztaD0e0sz#Bj4uEH literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/coin.png b/applications/external/jetpack_joyride/assets/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b5a409e43ce74ce6d84d83d8dda61c67b74d5b GIT binary patch literal 1842 zcma)6eM}Q)7(a20fx)N~U6hRT93aE?u7^~5_Q29sVbBIj8K7?K(Y|d@dLO$xXd#9v z_z~v(gDG*$uR*4Psl+U9#%#kab1c(se(Xct7KquhEsn70Viu=^y?5nP)a;M9*Z2AT zp3nDro|Y2l+QfuK2?&BD78clC@EfO|n3?eRwE4_x1c{x?x+|4RM=|9UI6dPNs)0Vj zi4aE+b6!Mbyfr{Ut3iO}Y3$Dr&R{6(qp^x?2jLK{AjlRpNT962>Gn3%cuhVmFE_y) zp`ZZ{C=43mLcB~xXiT$9!T+il$54%;)X-R^qXe}I5Dl(0( zu>Ob8Rb(0+*4p8CbQT;pB@tP%*TF)J7IjKMV|mN&dJ*6*APcpU7uf3nuPoJS0liUH z2`~zf3~B)#fheS@@cy=3G;$oWN+zWW*;qshS3<GVpTr7QKd8 zZFo>rYaxp9s{UzAlmuU`7x=Ugpt&e23w|ZcNWc<+-9uv*zn_JX)}wGP*#HkDltf8A zpanAF5#Nrw^+VCa;0;)>4(RUH@~&4DY*WRPGjmRIg7w9h`(<7YoBRHdzPog z-?qR1mu(w*yX;MkpKl2DU%Iz7Y1Ipx(|dlu5y?sJ?D=TmvZt@+RMpYp)2|%oGP*M_ zUc}5ty4ICOTsbFQZb$#Tq36k)sn?%m_NFbI-)p#SYdhp{R^0iHRyMThYekVuJ-qX@2e8;HO{BdAh1Jec!VOHEt*;&Lurxi#x9aVzbgH4b@b3zz7`H?FH*{Pp)8zrDDt=BChH`qS&reRAHQ zoc*{l_V}#t4*okRD{+^~@4QoV_1d->CSm=(m{t1%mxpt9!^c44ON!lg_#GJ;Nwa@} zo`{6uWv1eCmW_-cSI#9`RE}6sB_qh}v+9}AmN|Pd6fP*VS=>*wdqof9srAf>-;AL@ aZiz(}u8qk%++N2+22yBu+IsRmP5%LQYG}3q literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/coin_infill.png b/applications/external/jetpack_joyride/assets/coin_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..ab37874ff8b25de5a5d955a376dff3534ee4ed41 GIT binary patch literal 1974 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 literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/dead_scientist_infill.png b/applications/external/jetpack_joyride/assets/dead_scientist_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..6f036fde2f35d695c8eb2bb4ec27bb84f44de90c GIT binary patch literal 1900 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 literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/pillar.png b/applications/external/jetpack_joyride/assets/pillar.png new file mode 100644 index 0000000000000000000000000000000000000000..61979b393acb5f4397d25af4c2590f7f4b4bb1cc GIT binary patch literal 1880 zcma)74Qvxt950GE%J>Mv&?#~p$TpYWUAuLqXK$oy3oWd3jv^Zw8@KkgJ=Wfxch~h+ zl&^$OLk1fk8WBe_5&75*i%~I#5tNTPaPqO>bbjE7K!9mP#USdt>*zKch)rJG_kO?k z`+vOu|Gf>xMRP`L)3q9nX0*#`FM+>f;mA#S2!3~u{7uwo9+}8^N~KbFA?fA9813V# z049fdh-);KSu#(1tAT`6fdIo&Xy3<2P=xVO=t85LaPu}0WSlVp%#Rg$ys>Jp*@w=` z*IHx}3WR|~BXT&zilj`TsxArtE6X^Fs1&K1LQCDnh>a5fV#JIXf#z!wi{SH0wF&v91 znsgNxkth^}IC9UkFrN@-#s27F9&nlFaXm)hgUNi5lQ=QR-DgVzPTB}$W*~+kW)vie zE;0W5&=n+!4s-2xKRAkTct{|k}eZ z+5|vLzzzq68VEyyvyxgPyYk}#3EGqVW;K_tj-&d2y`??X14AekJ@hUMX-CHnvH z6A333cHAjgnh*47V2_OpaRPJ>#*m{rI{>+Cwqk+vGa-nHC36Z8m&2B)H|OQ)447W! zD(6NPGcpL3+LL}Xdo`AoGj@I(*dxM@kzmi*1&op zU;_%!ucHFW`Gg`$fiGYgc%pF=DpWdvQK&f&A7rL12~t`Hv8aVaIvG*ogj#he-beGn zOYvEJ8e-H2LJ+l#NZPB|r%+ztd=W43sZm0?I4W{}DM|~#8h{l5pz65vf%(s8;%qZzi6f%TA72O3T0mdKXR=M`8dteZ83$~@MXrYm=P_NQiV?jD}? zg4O<-wPjO9<*M$>70)zV&2+NzE}S68#LLdX7p*J&z|Bvx641QY#62e?u}Se2R9?5{V(sKH0_AC zZtQx|dA@1wwx;cA+jpF6D7w70{gsX#Gpk--Q@f$(5+#E!uZt`f*3yDd!f`oqs-PiM=fEE${hZ-P%dV zZeLlmXL4p*>W_CW5*ss?EL*$3(757m%Itgx?s^`n>bB2Xy^}n|b X8$D^dPo8AzRUSl_qsZP|uw?bWG&6$} literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_left.png b/applications/external/jetpack_joyride/assets/scientist_left.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e880b6b2e1b8ee4a19c47c48b66bbcf88bb29c GIT binary patch literal 2056 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 B(K-MC literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_right.png b/applications/external/jetpack_joyride/assets/scientist_right.png new file mode 100644 index 0000000000000000000000000000000000000000..dc40b560de9f5d5212491fe62a09f849a9ba8b56 GIT binary patch literal 1917 zcma)7dr%a09AAwX#B2g28T)5j8u8KH-Y#&=Ew>QdAsn)VaN?n8F1LGkD|frs?&9sC zW9EdBj}RZ3jyd8Vj-e&K(k#Pp%6!kL;E;`GW20r`BMe4kq~GrH6s-Q(yZwDXpYQ9l z-~H|fMTNQJq9;Z}5H!w_XDUi>j5H%uutp^i z1ULj)GnF9iu4HAng7tDdiQN0D8G$*5L}sT_CMsxSeOz9($d*(WI^ETkZVQ8CW<*;R z0t5)KG7T#MKQ9pqiD+>NaIczC1lA~WC5gDGBG@K~ESzdgHJXr&XxJ(;9-`Pjz1JC> zNW>@0K>|fXp^!0@Y!pNO$BA zzaW-?(O^jg8!#7f3hQ^-xd6*cKy(tZJcX4F!mtz#?AJ^WN(l^znfw&wu-S@4!Nd6h zCKcyq!;T!A8MBzp2HdFUS7js|0ouzJu)J6Hfx7hg1Hf#KQSEw7npthnU!~5+ES>{` z;96P@at&Z4l7^#$vXqSofk#GAV73`rrtP#03dK!$vcZ&Uz%eI|6Ui8XrA;&8gvq33 z(f9(xdBXq2R_7VE0zO3%d7LB*VpucjLtetZt)KLxpVOvWoo|&-yVdSUBq$0@m78U> z)Y|l`s3dsg5G}GQ}5wnf~a|=5WW1bE~f`PCQlGJ~iLpJ?H*}rde(IF;m+2Pcj!=2zwSz z+AO7aExFruO*-0D-js_kY}@r9%e?8ziH7ZXdfMJ2^Me-)UpW-9qp@M!n-o>lnsIMm z*@L#q*2blG->R%ldLiCl)naShd8_8+dr74oTfR$g2`nTgPRNMsye($kNS{#`|ETpB zXMMAwE%8Q7)8VLTpGJQ;t7%KyftEkTpW4r^oO879uXW$sdC#1RO-fQEogFbV!Mh=* z`(e!5_PR|=tJ$25ORft~UOTgH_vtb&>Tl6#R?%;Uc(=(zJk%Gi@XE6dN6Ti$u}Qe>w6`qaw1&-tz`>)5^j<*PNT zsN2fr9n}wKi?vtRJCyXAofhA*cOTbOT*&`>tMv_L-Z#Hb?CfmFJ)ed?fw<$|TceZ8mW*G@S8qnqOke}oSv zG<_C(oIEmrv}M7WHIXyOWpks?mM`vFbn4qn=NzSz<1Zh))19ADzHMdmw%>4b5_e0m ch89C7N^}2Mv+B#a>Q~c|Q)vG@d%^O50K+qvi~s-t literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_right_infill.png b/applications/external/jetpack_joyride/assets/scientist_right_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..e4bc7def8808e256ca957c218ce88ae9ab722036 GIT binary patch literal 2210 zcmcIlYj6`)6yCz<(3T=jd4srawba&RA0cV8BrR`o zr=<6~s7@)QPsP^!$}puk3qc+gIgeNIy64h9A!KA& zp&l4xArayTVk?S?jIR+WXgvsUVLSH6{sS1wv39K1>?Yh|DF|}qaS7DKD?Gk zMTKd$7!3(RKw;2Wh!4wj%#LZiH2kk_;~1)$D2;Z^>#jsg1qq;LquEGcg=wfwV*PZr zW6ppxT-mXpqKGt(N25_=)MON-08U!1R-B-4iZVb8gWMEWn3y3fXQ>oAhXcqyi4zr0 z2%{<|Q!hjmJBC3V9UuoW?k;;+9w;9QfvX5k8VNkvTnq||AO{68kX>h24{7SnDx3oN5B z4n~2AQUql(5M~3F?;!}~RCw-Fg(Y@};XIS?K^s(XU88+BH8$P_jHr7WgoWg^ob{F%?PdWXEbucCUcn0e5)%ST2~rQja{~-o$UzDlp3JXXCLOWRMc;!*H@pMz#3cY1iE9Le z;oTv@OJd>JhTjn+rImw|I&4A_BxikOeRrOtYTWT6Q`@AfEZ2n0BWWE~nNvabyF0kG zHyTZSS2k~X^5fh74g99wanD#<41cA6m)rPW+v|K?=G2>8f7#W^p6dOfZ{LOH?qAQ# zho)ELP3}53vXftu-f?HK+?T%R+>D0c&haxAKklq8TYc!++;evf7Sod3ouB2tbLZ@o z`sIsL7QJ=7ZF@u0rPNEiHhtM};|=lE4edwQ%v;lJ-nZ>~$F4F%i^ah~iBT{&D&sNnn7&zsHLoh>_Z zmp5Z6`0>*PkDbc3vo>+7C@@^v&m>9qO!nfb#gSP`ePk7PP0WbU1%DEmV zeE0YFXFE@zdt*_!7^zfO)b#fwXHRAnt2SbJos1yqnd*~*Y}q;qZsxcg#hy`}zEK&G zW#Y0&>c{quI+TJwckSTC!&eVp%xOQka`vHX3+>%;chDBm z=?DWh4dD+qokSL55FK$67bL+*2wM!Y4`jmP$7cVSae{6TH#al4`rZ`^SXunzKHm5D zdp_UidEPctNkN<BTHI^d)W8+cIGP*QGfsIh8nSnf_m zj+cZA+(4vIuiM24q*sf{dP(>%ZQ~d!Q^XoAR%tY$I+h1$hB8B`!geaq9NuOp&H7!7 z!f>UqISvK^$H3%*}H#Ar%ejEo zNF5I-5$NHg#RwIVo~Fu3Q)iSDYEqp+rf2U^5h_)PY-DY;-S=N)BAZOlf@E?f8aYX+F}UWf>joVtMEsj3Hfib{Q(t=}bIpr(F;e%mw*qkwKTK)?{X; z5lXepmClVc(O%%H)YERj2(ZSqm^v8sGH6)?lx>&6)`A9t3Z;`Ji`}u% z+Tbd)lU;!7f~dgSMGwV;JO`{GEtY4u)6i2N3VV}w zFn~u1lu)Xa&##v9I04kS`XEq54W!0H=218{r+C+ZG5ug)6MG*8_rhL7`Ao_7o#_*KRbW%^O38G z^^Va~y|>R)%|!iLJfmoTtn=L){pN*2&Fe{hJ*Ai30zvecr>-&cLkp??nbg;A?muN| z{ekqRHn)hEzgD!zl=j<<$FrKZ-uYkbd|Qk|Mc;MsE6El=CP;dXJU7> z?SJC_xrV9Tu@e)t@xxAT~bYY)eKQ<~L8+)X&yQet@- z8<~6I;E|N^X^=Ti4HO>QW+C-e$7XBW4&SIve3oYdfj_4+8(vjR*EE?P`m=GjX&|E8 z_m%G_PuHc$`Kx1=-gidMJRW(v=a-{<=al!Z_^*G_cW%%xK6&D1@r9``Q@V$)sQiCL z&-P4mG1Efx`wxB-ys@(%H%>>nZoN3%b@$T22tLU%H!=8oHoTHzxI&A7Ah8M36M>xQ z+5$IKMf$w*EuGfC1A*M+ + +#include "background_assets.h" + +static AssetProperties assetProperties[BG_ASSETS_MAX] = { + {.width = 27, .spawn_chance = 1, .x_offset = 24, .y_offset = 36, .sprite = &I_door}, + {.width = 12, .spawn_chance = 6, .x_offset = 33, .y_offset = 14, .sprite = &I_air_vent}}; + +void background_assets_tick(BackgroundAsset* const assets) { + // Move assets towards the player + for(int i = 0; i < BG_ASSETS_MAX; i++) { + if(assets[i].visible) { + assets[i].point.x -= 1; // move left by 2 units + if(assets[i].point.x <= + -assets[i].properties->width) { // if the asset is out of screen + assets[i].visible = false; // set asset x coordinate to 0 to mark it as "inactive" + } + } + } +} + +void spawn_random_background_asset(BackgroundAsset* const assets) { + // Calculate the total spawn chances for all assets + int total_spawn_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + total_spawn_chance += assetProperties[i].spawn_chance; + } + + // Generate a random number between 0 and total_spawn_chance + int random_number = rand() % total_spawn_chance; + + // Select the asset based on the random number + int chosen_asset = -1; + int accumulated_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + accumulated_chance += assetProperties[i].spawn_chance; + if(random_number < accumulated_chance) { + chosen_asset = i; + break; + } + } + + // If no asset is chosen, return + if(chosen_asset == -1) { + return; + } + + // Look for an available slot for the chosen asset + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible == false) { + // Spawn the asset + assets[i].point.x = 127 + assetProperties[chosen_asset].x_offset; + assets[i].point.y = assetProperties[chosen_asset].y_offset; + assets[i].properties = &assetProperties[chosen_asset]; + assets[i].visible = true; + break; + } + } +} + +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance) { + canvas_draw_box(canvas, 0, 6, 128, 1); + canvas_draw_box(canvas, 0, 56, 128, 2); + + // Calculate the pillar offset based on the traveled distance + int pillar_offset = distance % 64; + + // Draw pillars + for(int x = -pillar_offset; x < 128; x += 64) { + canvas_draw_icon(canvas, x, 6, &I_pillar); + } + + // Draw assets + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, assets[i].point.x, assets[i].point.y, assets[i].properties->sprite); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/background_assets.h b/applications/external/jetpack_joyride/includes/background_assets.h new file mode 100644 index 000000000..d42fcfd71 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/background_assets.h @@ -0,0 +1,34 @@ +#ifndef BACKGROUND_ASSETS_H +#define BACKGROUND_ASSETS_H + +#include +#include + +#include + +#include "point.h" +#include "states.h" +#include "game_sprites.h" +#include + +#define BG_ASSETS_MAX 3 + +typedef struct { + int width; + int spawn_chance; + int x_offset; + int y_offset; + const Icon* sprite; +} AssetProperties; + +typedef struct { + POINT point; + AssetProperties* properties; + bool visible; +} BackgroundAsset; + +void background_assets_tick(BackgroundAsset* const assets); +void spawn_random_background_asset(BackgroundAsset* const assets); +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance); + +#endif // BACKGROUND_ASSETS_H diff --git a/applications/external/jetpack_joyride/includes/barry.c b/applications/external/jetpack_joyride/includes/barry.c new file mode 100644 index 000000000..61d3a6fc4 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.c @@ -0,0 +1,33 @@ +#include "barry.h" +#include "game_sprites.h" + +#include +#include + +void barry_tick(BARRY* const barry) { + // Do jetpack things + if(barry->isBoosting) { + barry->gravity += GRAVITY_BOOST; // Increase upward momentum + } else { + barry->gravity += GRAVITY_FALL; // Increase downward momentum faster + } + + barry->point.y += barry->gravity; + + // Constrain barry's height within sprite_height and 64 - sprite_height + if(barry->point.y > (64 - BARRY_HEIGHT)) { + barry->point.y = 64 - BARRY_HEIGHT; + barry->gravity = 0; // stop upward momentum + } else if(barry->point.y < 0) { + barry->point.y = 0; + barry->gravity = 0; // stop downward momentum + } +} + +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon_animation(canvas, barry->point.x, barry->point.y, sprites->barry); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, barry->point.x, barry->point.y, sprites->barry_infill); +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/barry.h b/applications/external/jetpack_joyride/includes/barry.h new file mode 100644 index 000000000..494af434d --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.h @@ -0,0 +1,23 @@ +#ifndef BARRY_H +#define BARRY_H + +#include + +#include +#include "point.h" +#include "game_sprites.h" + +#define GRAVITY_TICK 0.2 +#define GRAVITY_BOOST -0.4 +#define GRAVITY_FALL 0.3 + +typedef struct { + float gravity; + POINT point; + bool isBoosting; +} BARRY; + +void barry_tick(BARRY* const barry); +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites); + +#endif // BARRY_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/coin.c b/applications/external/jetpack_joyride/includes/coin.c new file mode 100644 index 000000000..7a3811a8c --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +#include "coin.h" +#include "barry.h" + +#define PATTERN_MAX_HEIGHT 40 + +// Patterns +const COIN_PATTERN coin_patterns[] = { + {// Square pattern + .count = 9, + .coins = {{0, 0}, {8, 0}, {16, 0}, {0, 8}, {8, 8}, {16, 8}, {0, 16}, {8, 16}, {16, 16}}}, + {// Wavy pattern (approximate sine wave) + .count = 8, + .coins = {{0, 8}, {8, 16}, {16, 24}, {24, 16}, {32, 8}, {40, 0}, {48, 8}, {56, 16}}}, + {// Diagonal pattern + .count = 5, + .coins = {{0, 0}, {8, 8}, {16, 16}, {24, 24}, {32, 32}}}, + // Add more patterns here +}; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const total_coins) { + // Move coins towards the player + for(int i = 0; i < COINS_MAX; i++) { + if(coin_colides(&coins[i], barry)) { + coins[i].point.x = 0; // Remove the coin + (*total_coins)++; + } + if(coins[i].point.x > 0) { + coins[i].point.x -= 1; // move left by 1 unit + if(coins[i].point.x < -COIN_WIDTH) { // if the coin is out of screen + coins[i].point.x = 0; // set coin x coordinate to 0 to mark it as "inactive" + } + } + } +} + +bool coin_colides(COIN* const coin, BARRY* const barry) { + return !( + barry->point.x > coin->point.x + COIN_WIDTH || // Barry is to the right of the coin + barry->point.x + BARRY_WIDTH < coin->point.x || // Barry is to the left of the coin + barry->point.y > coin->point.y + COIN_WIDTH || // Barry is below the coin + barry->point.y + BARRY_HEIGHT < coin->point.y); // Barry is above the coin +} + +void spawn_random_coin(COIN* const coins) { + // Select a random pattern + int pattern_index = rand() % (sizeof(coin_patterns) / sizeof(coin_patterns[0])); + const COIN_PATTERN* pattern = &coin_patterns[pattern_index]; + + // Count available slots for new coins + int available_slots = 0; + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x <= 0) { + ++available_slots; + } + } + + // If there aren't enough slots, return without spawning coins + if(available_slots < pattern->count) return; + + // Spawn coins according to the selected pattern + int coin_index = 0; + int random_offset = rand() % (SCREEN_HEIGHT - PATTERN_MAX_HEIGHT); + int random_offset_x = rand() % 16; + for(int i = 0; i < pattern->count; ++i) { + // Find an available slot for a new coin + while(coins[coin_index].point.x > 0 && coin_index < COINS_MAX) { + ++coin_index; + } + // If no slot is available, stop spawning coins + if(coin_index == COINS_MAX) break; + + // Spawn the coin + coins[coin_index].point.x = SCREEN_WIDTH - 1 + pattern->coins[i].x + random_offset_x; + coins[coin_index].point.y = + random_offset + + pattern->coins[i] + .y; // The pattern is spawned at a random y position, but not too close to the screen edge + } +} + +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x > 0) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin_infill); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/coin.h b/applications/external/jetpack_joyride/includes/coin.h new file mode 100644 index 000000000..41fd21ddc --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.h @@ -0,0 +1,26 @@ +#ifndef COIN_H +#define COIN_H + +#include + +#include "point.h" +#include "barry.h" + +#define COINS_MAX 15 + +typedef struct { + float gravity; + POINT point; +} COIN; + +typedef struct { + int count; + POINT coins[COINS_MAX]; +} COIN_PATTERN; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const poins); +void spawn_random_coin(COIN* const coins); +bool coin_colides(COIN* const coin, BARRY* const barry); +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites); + +#endif // COIN_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_sprites.h b/applications/external/jetpack_joyride/includes/game_sprites.h new file mode 100644 index 000000000..d38494bf8 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_sprites.h @@ -0,0 +1,35 @@ +#ifndef GAME_SPRITES_H +#define GAME_SPRITES_H + +#include "point.h" +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +#define BARRY_WIDTH 11 +#define BARRY_HEIGHT 15 + +#define MISSILE_WIDTH 26 +#define MISSILE_HEIGHT 12 + +#define SCIENTIST_WIDTH 9 +#define SCIENTIST_HEIGHT 14 + +#define COIN_WIDTH 7 + +typedef struct { + IconAnimation* barry; + const Icon* barry_infill; + const Icon* scientist_left; + const Icon* scientist_left_infill; + const Icon* scientist_right; + const Icon* scientist_right_infill; + const Icon* coin; + const Icon* coin_infill; + IconAnimation* missile; + IconAnimation* alert; + const Icon* missile_infill; +} GameSprites; + +#endif // GAME_SPRITES_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_state.c b/applications/external/jetpack_joyride/includes/game_state.c new file mode 100644 index 000000000..a8a9db618 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.c @@ -0,0 +1,5 @@ +#include "game_state.h" + +void game_state_tick(GameState* const game_state) { + game_state->distance++; +} diff --git a/applications/external/jetpack_joyride/includes/game_state.h b/applications/external/jetpack_joyride/includes/game_state.h new file mode 100644 index 000000000..1e97aaf18 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.h @@ -0,0 +1,34 @@ +#ifndef GAMESTATE_H +#define GAMESTATE_H + +#include +#include + +#include "barry.h" +#include "scientist.h" +#include "coin.h" +#include "particle.h" +#include "game_sprites.h" +#include "states.h" +#include "missile.h" +#include "background_assets.h" +typedef struct { + int total_coins; + int distance; + bool new_highscore; + BARRY barry; + COIN coins[COINS_MAX]; + PARTICLE particles[PARTICLES_MAX]; + SCIENTIST scientists[SCIENTISTS_MAX]; + MISSILE missiles[MISSILES_MAX]; + BackgroundAsset bg_assets[BG_ASSETS_MAX]; + State state; + GameSprites sprites; + FuriMutex* mutex; + FuriTimer* timer; + void (*death_handler)(); +} GameState; + +void game_state_tick(GameState* const game_state); + +#endif // GAMESTATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/missile.c b/applications/external/jetpack_joyride/includes/missile.c new file mode 100644 index 000000000..af47e8478 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.c @@ -0,0 +1,86 @@ +#include +#include + +#include +#include + +#include "states.h" +#include "game_sprites.h" +#include "missile.h" +#include "barry.h" + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()) { + // Move missiles towards the player + for(int i = 0; i < MISSILES_MAX; i++) { + if(missiles[i].visible && missile_colides(&missiles[i], barry)) { + death_handler(); + } + if(missiles[i].visible) { + missiles[i].point.x -= 2; // move left by 2 units + if(missiles[i].point.x < -MISSILE_WIDTH) { // if the missile is out of screen + missiles[i].visible = false; // set missile as "inactive" + } + } + } +} + +void spawn_random_missile(MISSILE* const missiles) { + // Check for an available slot for a new missile + for(int i = 0; i < MISSILES_MAX; ++i) { + if(!missiles[i].visible) { + missiles[i].point.x = 2 * SCREEN_WIDTH; + missiles[i].point.y = rand() % (SCREEN_HEIGHT - MISSILE_HEIGHT); + missiles[i].visible = true; + break; + } + } +} + +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < MISSILES_MAX; ++i) { + if(missiles[i].visible) { + canvas_set_color(canvas, ColorBlack); + + if(missiles[i].point.x > 128) { + canvas_draw_icon_animation( + canvas, SCREEN_WIDTH - 7, missiles[i].point.y, sprites->alert); + } else { + canvas_draw_icon_animation( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile_infill); + } + } + } +} + +bool missile_colides(MISSILE* const missile, BARRY* const barry) { + return !( + barry->point.x > + missile->point.x + MISSILE_WIDTH - 14 || // Barry is to the right of the missile + barry->point.x + BARRY_WIDTH - 3 < + missile->point.x || // Barry is to the left of the missile + barry->point.y > missile->point.y + MISSILE_HEIGHT || // Barry is below the missile + barry->point.y + BARRY_HEIGHT < missile->point.y); // Barry is above the missile +} + +int get_rocket_spawn_distance(int player_distance) { + // Define the start and end points for rocket spawn distance + int start_distance = 256; + int end_distance = 24; + + // Define the maximum player distance at which the spawn distance should be at its minimum + int max_player_distance = 5000; // Adjust this value based on your game's difficulty curve + + if(player_distance >= max_player_distance) { + return end_distance; + } + + // Calculate the linear interpolation factor + float t = (float)player_distance / max_player_distance; + + // Interpolate the rocket spawn distance + return start_distance + t * (end_distance - start_distance); +} diff --git a/applications/external/jetpack_joyride/includes/missile.h b/applications/external/jetpack_joyride/includes/missile.h new file mode 100644 index 000000000..a5af4e885 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.h @@ -0,0 +1,24 @@ +#ifndef MISSILE_H +#define MISSILE_H + +#include +#include "game_sprites.h" + +#include "states.h" +#include "point.h" +#include "barry.h" + +#define MISSILES_MAX 5 + +typedef struct { + POINT point; + bool visible; +} MISSILE; + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()); +void spawn_random_missile(MISSILE* const MISSILEs); +bool missile_colides(MISSILE* const MISSILE, BARRY* const barry); +int get_rocket_spawn_distance(int player_distance); +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites); + +#endif // MISSILE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/particle.c b/applications/external/jetpack_joyride/includes/particle.c new file mode 100644 index 000000000..cf8e6e0a6 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.c @@ -0,0 +1,57 @@ +#include + +#include "particle.h" +#include "scientist.h" +#include "barry.h" + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists) { + // Move particles + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + particles[i].point.y += PARTICLE_VELOCITY; + + // Check collision with scientists + for(int j = 0; j < SCIENTISTS_MAX; j++) { + if(scientists[j].state == ScientistStateAlive && scientists[j].point.x > 0) { + // Check whether the particle lies within the scientist's bounding box + if(!(particles[i].point.x > scientists[j].point.x + SCIENTIST_WIDTH || + particles[i].point.x < scientists[j].point.x || + particles[i].point.y > scientists[j].point.y + SCIENTIST_HEIGHT || + particles[i].point.y < scientists[j].point.y)) { + scientists[j].state = ScientistStateDead; + // (*points) += 2; // Increase the score by 2 + } + } + } + + if(particles[i].point.x < 0 || particles[i].point.x > SCREEN_WIDTH || + particles[i].point.y < 0 || particles[i].point.y > SCREEN_HEIGHT) { + particles[i].point.y = 0; + } + } + } +} + +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry) { + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y <= 0) { + particles[i].point.x = barry->point.x + (rand() % 4); + particles[i].point.y = barry->point.y + 14; + break; + } + } +} + +void draw_particles(const PARTICLE* particles, Canvas* const canvas) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + canvas_draw_line( + canvas, + particles[i].point.x, + particles[i].point.y, + particles[i].point.x, + particles[i].point.y + 3); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/particle.h b/applications/external/jetpack_joyride/includes/particle.h new file mode 100644 index 000000000..3442c9c4e --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.h @@ -0,0 +1,21 @@ + + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include "point.h" +#include "scientist.h" +#include "barry.h" + +#define PARTICLES_MAX 50 +#define PARTICLE_VELOCITY 2 + +typedef struct { + POINT point; +} PARTICLE; + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists); +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry); +void draw_particles(const PARTICLE* particles, Canvas* const canvas); + +#endif // PARTICLE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/point.h b/applications/external/jetpack_joyride/includes/point.h new file mode 100644 index 000000000..02c9a6ce4 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/point.h @@ -0,0 +1,14 @@ +#ifndef POINT_H +#define POINT_H + +typedef struct { + int x; + int y; +} POINT; + +typedef struct { + float x; + float y; +} POINTF; + +#endif // POINT_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.c b/applications/external/jetpack_joyride/includes/scientist.c new file mode 100644 index 000000000..b1a8a14c0 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.c @@ -0,0 +1,77 @@ +#include "scientist.h" +#include "game_sprites.h" + +#include +#include + +void scientist_tick(SCIENTIST* const scientists) { + for(int i = 0; i < SCIENTISTS_MAX; i++) { + if(scientists[i].visible) { + if(scientists[i].point.x < 64) scientists[i].velocity_x = 0.5f; + + scientists[i].point.x -= scientists[i].state == ScientistStateAlive ? + 1 - scientists[i].velocity_x : + 1; // move based on velocity_x + int width = (scientists[i].state == ScientistStateAlive) ? SCIENTIST_WIDTH : + SCIENTIST_HEIGHT; + if(scientists[i].point.x <= -width) { // if the scientist is out of screen + scientists[i].visible = false; + } + } + } +} + +void spawn_random_scientist(SCIENTIST* const scientists) { + float velocities[] = {-0.5f, 0.0f, 0.5f, -1.0f}; + // Check for an available slot for a new scientist + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(!scientists[i].visible && + (rand() % 1000) < 10) { // Spawn rate is less frequent than coins + scientists[i].state = ScientistStateAlive; + scientists[i].point.x = 127; + scientists[i].point.y = 49; + scientists[i].velocity_x = velocities[rand() % 4]; + scientists[i].visible = true; + break; + } + } +} + +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(scientists[i].visible) { + canvas_set_color(canvas, ColorBlack); + if(scientists[i].state == ScientistStateAlive) { + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right : + sprites->scientist_left); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right_infill : + sprites->scientist_left_infill); + + } else { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist_infill); + } + } + } +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.h b/applications/external/jetpack_joyride/includes/scientist.h new file mode 100644 index 000000000..a49e8028c --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.h @@ -0,0 +1,29 @@ +#ifndef SCIENTIST_H +#define SCIENTIST_H + +#include "point.h" +#include "game_sprites.h" +#include + +#define SCIENTIST_VELOCITY_MIN -0.5f +#define SCIENTIST_VELOCITY_MAX 0.5f + +#define SCIENTISTS_MAX 6 + +typedef enum { + ScientistStateAlive, + ScientistStateDead, +} ScientistState; + +typedef struct { + bool visible; + POINTF point; + float velocity_x; + ScientistState state; +} SCIENTIST; + +void scientist_tick(SCIENTIST* const scientist); +void spawn_random_scientist(SCIENTIST* const scientists); +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites); + +#endif // SCIENTIST_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/states.h b/applications/external/jetpack_joyride/includes/states.h new file mode 100644 index 000000000..d58e3e1f6 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/states.h @@ -0,0 +1,9 @@ +#ifndef STATE_H +#define STATE_H + +typedef enum { + GameStateLife, + GameStateGameOver, +} State; + +#endif // STATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/jetpack.c b/applications/external/jetpack_joyride/jetpack.c new file mode 100644 index 000000000..c12f094c9 --- /dev/null +++ b/applications/external/jetpack_joyride/jetpack.c @@ -0,0 +1,379 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "includes/point.h" +#include "includes/barry.h" +#include "includes/scientist.h" +#include "includes/particle.h" +#include "includes/coin.h" +#include "includes/missile.h" +#include "includes/background_assets.h" + +#include "includes/game_state.h" + +#define TAG "Jetpack Joyride" +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/jetpack.save" +static GameState* global_state; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +typedef struct { + int max_distance; + int total_coins; +} SaveGame; + +static SaveGame save_game; + +static bool storage_game_state_load() { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) + bytes_readed = storage_file_read(file, &save_game, sizeof(SaveGame)); + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SaveGame); +} + +static void storage_game_state_save() { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, &save_game, sizeof(SaveGame)); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +void handle_death() { + global_state->state = GameStateGameOver; + global_state->new_highscore = global_state->distance > save_game.max_distance; + + if(global_state->distance > save_game.max_distance) { + save_game.max_distance = global_state->distance; + } + + save_game.total_coins += global_state->total_coins; + + storage_game_state_save(); +} + +static void jetpack_game_state_init(GameState* const game_state) { + UNUSED(game_state); + UNUSED(storage_game_state_save); + BARRY barry; + barry.gravity = 0; + barry.point.x = 32 + 5; + barry.point.y = 32; + barry.isBoosting = false; + + GameSprites sprites; + sprites.barry = icon_animation_alloc(&A_barry); + sprites.barry_infill = &I_barry_infill; + + sprites.scientist_left = (&I_scientist_left); + sprites.scientist_left_infill = (&I_scientist_left_infill); + sprites.scientist_right = (&I_scientist_right); + sprites.scientist_right_infill = (&I_scientist_right_infill); + + sprites.coin = (&I_coin); + sprites.coin_infill = (&I_coin_infill); + + sprites.missile = icon_animation_alloc(&A_missile); + sprites.missile_infill = &I_missile_infill; + + sprites.alert = icon_animation_alloc(&A_alert); + + icon_animation_start(sprites.barry); + icon_animation_start(sprites.missile); + icon_animation_start(sprites.alert); + + game_state->barry = barry; + game_state->total_coins = 0; + game_state->distance = 0; + game_state->new_highscore = false; + game_state->sprites = sprites; + game_state->state = GameStateLife; + game_state->death_handler = handle_death; + + memset(game_state->bg_assets, 0, sizeof(game_state->bg_assets)); + + memset(game_state->scientists, 0, sizeof(game_state->scientists)); + memset(game_state->coins, 0, sizeof(game_state->coins)); + memset(game_state->particles, 0, sizeof(game_state->particles)); + memset(game_state->missiles, 0, sizeof(game_state->missiles)); +} + +static void jetpack_game_state_free(GameState* const game_state) { + icon_animation_free(game_state->sprites.barry); + icon_animation_free(game_state->sprites.missile); + icon_animation_free(game_state->sprites.alert); + + free(game_state); +} + +static void jetpack_game_tick(GameState* const game_state) { + if(game_state->state == GameStateGameOver) return; + barry_tick(&game_state->barry); + game_state_tick(game_state); + coin_tick(game_state->coins, &game_state->barry, &game_state->total_coins); + particle_tick(game_state->particles, game_state->scientists); + scientist_tick(game_state->scientists); + missile_tick(game_state->missiles, &game_state->barry, game_state->death_handler); + + background_assets_tick(game_state->bg_assets); + + // generate background every 64px aka. ticks + if(game_state->distance % 64 == 0 && rand() % 3 == 0) { + spawn_random_background_asset(game_state->bg_assets); + } + + if(game_state->distance % 48 == 0 && rand() % 2 == 0) { + spawn_random_coin(game_state->coins); + } + + if(game_state->distance % get_rocket_spawn_distance(game_state->distance) == 0 && + rand() % 2 == 0) { + spawn_random_missile(game_state->missiles); + } + + spawn_random_scientist(game_state->scientists); + + if(game_state->barry.isBoosting) { + spawn_random_particles(game_state->particles, &game_state->barry); + } +} + +static void jetpack_game_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state->state == GameStateLife) { + canvas_set_bitmap_mode(canvas, false); + + draw_background_assets(game_state->bg_assets, canvas, game_state->distance); + + canvas_set_bitmap_mode(canvas, true); + + draw_coins(game_state->coins, canvas, &game_state->sprites); + draw_scientists(game_state->scientists, canvas, &game_state->sprites); + draw_particles(game_state->particles, canvas); + draw_missiles(game_state->missiles, canvas, &game_state->sprites); + + draw_barry(&game_state->barry, canvas, &game_state->sprites); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%u m", game_state->distance / 10); + canvas_draw_str_aligned(canvas, 123, 15, AlignRight, AlignBottom, buffer); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_draw_str_aligned(canvas, 5, 15, AlignLeft, AlignBottom, buffer); + } + + if(game_state->state == GameStateGameOver) { + // Show highscore + char buffer[64]; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "You flew"); + + snprintf( + buffer, + sizeof(buffer), + game_state->new_highscore ? "%u m (new best)" : "%u m", + game_state->distance / 10); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, buffer); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, "and collected"); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignTop, buffer); + + snprintf( + buffer, + sizeof(buffer), + "Best: %u m, Tot: $%u", + save_game.max_distance / 10, + save_game.total_coins); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 63, AlignCenter, AlignBottom, buffer); + + canvas_draw_rframe(canvas, 0, 3, 128, 49, 5); + + // char buffer[12]; + // snprintf(buffer, sizeof(buffer), "Dist: %u", game_state->distance); + // canvas_draw_str_aligned(canvas, 123, 12, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); + // canvas_draw_str_aligned(canvas, 5, 12, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, "Highscore:"); + // snprintf(buffer, sizeof(buffer), "Dist: %u", save_game.max_distance); + // canvas_draw_str_aligned(canvas, 123, 50, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", save_game.max_score); + // canvas_draw_str_aligned(canvas, 5, 50, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "boom."); + + // if(furi_timer_is_running(game_state->timer)) { + // furi_timer_start(game_state->timer, 0); + // } + } + + // canvas_draw_frame(canvas, 0, 0, 128, 64); + + furi_mutex_release(game_state->mutex); +} + +static void jetpack_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void jetpack_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t jetpack_game_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + if(!storage_game_state_load()) { + memset(&save_game, 0, sizeof(save_game)); + } + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + + global_state = game_state; + jetpack_game_state_init(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, jetpack_game_render_callback, game_state); + view_port_input_callback_set(view_port, jetpack_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(jetpack_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); + + game_state->timer = timer; + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeRelease && event.input.key == InputKeyOk) { + game_state->barry.isBoosting = false; + } + + // Reset highscore, for debug purposes + if(event.input.type == InputTypeLong && event.input.key == InputKeyLeft) { + save_game.max_distance = 0; + save_game.total_coins = 0; + storage_game_state_save(); + } + + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + if(game_state->state == GameStateGameOver) { + jetpack_game_state_init(game_state); + } + + if(game_state->state == GameStateLife) { + // Do something + game_state->barry.isBoosting = true; + } + + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + jetpack_game_tick(game_state); + } + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_mutex_free(game_state->mutex); + +free_and_exit: + jetpack_game_state_free(game_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/nfc_maker/application.fam b/applications/external/nfc_maker/application.fam new file mode 100644 index 000000000..89bbb202e --- /dev/null +++ b/applications/external/nfc_maker/application.fam @@ -0,0 +1,14 @@ +App( + appid="nfc_maker", + name="NFC Maker", + apptype=FlipperAppType.EXTERNAL, + entry_point="nfc_maker", + cdefines=["APP_NFC_MAKER"], + requires=[ + "storage", + "gui", + ], + stack_size=1 * 1024, + fap_icon="nfc_maker_10px.png", + fap_category="NFC", +) diff --git a/applications/external/nfc_maker/nfc_maker.c b/applications/external/nfc_maker/nfc_maker.c new file mode 100644 index 000000000..578054ade --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.c @@ -0,0 +1,81 @@ +#include "nfc_maker.h" + +static bool nfc_maker_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcMaker* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_maker_back_event_callback(void* context) { + furi_assert(context); + NfcMaker* app = context; + + return scene_manager_handle_back_event(app->scene_manager); +} + +NfcMaker* nfc_maker_alloc() { + NfcMaker* app = malloc(sizeof(NfcMaker)); + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher and Scene Manager + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_maker_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, nfc_maker_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_maker_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Gui Modules + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewSubmenu, submenu_get_view(app->submenu)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewTextInput, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewByteInput, byte_input_get_view(app->byte_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, NfcMakerViewPopup, popup_get_view(app->popup)); + + return app; +} + +void nfc_maker_free(NfcMaker* app) { + furi_assert(app); + + // Gui modules + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewTextInput); + text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewByteInput); + byte_input_free(app->byte_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewPopup); + popup_free(app->popup); + + // View Dispatcher and Scene Manager + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Records + furi_record_close(RECORD_GUI); + free(app); +} + +extern int32_t nfc_maker(void* p) { + UNUSED(p); + NfcMaker* app = nfc_maker_alloc(); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneMenu); + view_dispatcher_run(app->view_dispatcher); + nfc_maker_free(app); + return 0; +} diff --git a/applications/external/nfc_maker/nfc_maker.h b/applications/external/nfc_maker/nfc_maker.h new file mode 100644 index 000000000..388dc7196 --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/nfc_maker_scene.h" +#include +#include +#include +#include + +#define TEXT_INPUT_LEN 248 +#define WIFI_INPUT_LEN 90 + +typedef enum { + WifiAuthenticationOpen = 0x01, + WifiAuthenticationWpa2Personal = 0x20, + WifiAuthenticationWpa2Enterprise = 0x10, + WifiAuthenticationWpaPersonal = 0x02, + WifiAuthenticationWpaEnterprise = 0x08, + WifiAuthenticationShared = 0x04, +} WifiAuthentication; + +typedef enum { + WifiEncryptionAes = 0x08, + WifiEncryptionWep = 0x02, + WifiEncryptionTkip = 0x04, + WifiEncryptionNone = 0x01, +} WifiEncryption; + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + TextInput* text_input; + ByteInput* byte_input; + Popup* popup; + + uint8_t mac_buf[GAP_MAC_ADDR_SIZE]; + char text_buf[TEXT_INPUT_LEN]; + char pass_buf[WIFI_INPUT_LEN]; + char name_buf[TEXT_INPUT_LEN]; +} NfcMaker; + +typedef enum { + NfcMakerViewSubmenu, + NfcMakerViewTextInput, + NfcMakerViewByteInput, + NfcMakerViewPopup, +} NfcMakerView; diff --git a/applications/external/nfc_maker/nfc_maker_10px.png b/applications/external/nfc_maker/nfc_maker_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e2443f1790f0b3a595767feb8622ff3cf7ab31 GIT binary patch literal 4142 zcmeH~d2ka|9LIxVTMCs!E&*YiGEkAt9@%WNYg4F6OCt@Gl4;8T%4YYa-D#2y$)-u` zw47xu$T0}YB`Bg*1cm`RGNYB5wt}_gK2$E{vgI0()=@?}_;z~_%s8Eq|C-6M``&jy z@Avz@-+Pl+nm%nxO!SCojYbn=OSNXeU*$SFsyF-||9T`1f2s=|*>VOKLVSM7CAtA3 z7x(}I!lFx~37_|*Ck{&dxC;rWUClX9DjCN+(S z-&lHktbO*>h|F`R&^6Z^M%Gu4+?E%2=*-Ol%h%5{CafRY{Em4{L~(G%t(w%!v2|HV z%%dBw!Tq^3$HeT))T*fOOGZVQ$9X24emLxR!`#^7+%*k!Egrc0L8EYC_RfkW8JWUZ zeqlxAwS_0p+`})EBktaw=cwOMJ@mnWvR9fS=O3H3`mdt%vPRP@PPACkZ5B&gJ}`?d zAFZi)vwHI2l0&=pE&rlqK}q(Gz4FwVvkp$^ofLa`$ei8#Y(s~=KYZ=P71D2;8>-x~ zQQs}DSX`d7y`)4Ob#Tk-+KT$Reh2Q1O{@K-D0Tl%U$gh|z6IcT#YtC5FhBdnO7dRe zi!lbZ|}mUg?=!zaGHZJ%;OnB zJ1H?bARSDF^xp zy@KVbdWFQRGx%y(bto4o(*q4daTXrlD68BWs|7KTo$8idH z;lH2|JS;VxS#%U0w4QTLonqCjpU_}^2=Ds%QfCD;n!baSPp?y#iXXwoNZDpjj;xOu zGz3MDUV0Bxj%PM&k|XM;xvOgWXz+ej*H1KO<&W|FaOK0e!F$~)OG{Ty9y#Kaqp_bg t_Wr$-tJBizZ`5U#ZOn{0H@d&CSIm|E10x2OB9No8B~P>Nd1Kz + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcMakerScene##id, +typedef enum { +#include "nfc_maker_scene_config.h" + NfcMakerSceneNum, +} NfcMakerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_maker_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c new file mode 100644 index 000000000..4e70a184d --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void nfc_maker_scene_bluetooth_byte_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void nfc_maker_scene_bluetooth_on_enter(void* context) { + NfcMaker* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Enter Bluetooth MAC:"); + + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE; i++) { + app->mac_buf[i] = 0x69; + } + + byte_input_set_result_callback( + byte_input, + nfc_maker_scene_bluetooth_byte_input_callback, + NULL, + app, + app->mac_buf, + GAP_MAC_ADDR_SIZE); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewByteInput); +} + +bool nfc_maker_scene_bluetooth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + furi_hal_bt_reverse_mac_addr(app->mac_buf); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_bluetooth_on_exit(void* context) { + NfcMaker* app = context; + byte_input_set_result_callback(app->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(app->byte_input, ""); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h new file mode 100644 index 000000000..a89b4198c --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h @@ -0,0 +1,13 @@ +ADD_SCENE(nfc_maker, menu, Menu) +ADD_SCENE(nfc_maker, bluetooth, Bluetooth) +ADD_SCENE(nfc_maker, https, Https) +ADD_SCENE(nfc_maker, mail, Mail) +ADD_SCENE(nfc_maker, phone, Phone) +ADD_SCENE(nfc_maker, text, Text) +ADD_SCENE(nfc_maker, url, Url) +ADD_SCENE(nfc_maker, wifi, Wifi) +ADD_SCENE(nfc_maker, wifi_auth, WifiAuth) +ADD_SCENE(nfc_maker, wifi_encr, WifiEncr) +ADD_SCENE(nfc_maker, wifi_pass, WifiPass) +ADD_SCENE(nfc_maker, name, Name) +ADD_SCENE(nfc_maker, result, Result) diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c new file mode 100644 index 000000000..77b32afe1 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_https_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_https_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter HTTPS Link:"); + + strlcpy(app->text_buf, "google.com", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_https_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_https_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_https_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c new file mode 100644 index 000000000..98c648f6a --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_mail_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_mail_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter EMail Address:"); + + strlcpy(app->text_buf, "ben.dover@example.com", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_mail_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_mail_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_mail_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c new file mode 100644 index 000000000..4268e7e2f --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c @@ -0,0 +1,61 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_menu_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_menu_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "NFC Tag Maker:"); + + submenu_add_item( + submenu, + "Bluetooth MAC", + NfcMakerSceneBluetooth, + nfc_maker_scene_menu_submenu_callback, + app); + + submenu_add_item( + submenu, "HTTPS Link", NfcMakerSceneHttps, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Mail Address", NfcMakerSceneMail, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Phone Number", NfcMakerScenePhone, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Text Note", NfcMakerSceneText, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Plain URL", NfcMakerSceneUrl, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "WiFi Login", NfcMakerSceneWifi, nfc_maker_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_menu_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneMenu, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, event.event); + } + + return consumed; +} + +void nfc_maker_scene_menu_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c new file mode 100644 index 000000000..dd5170d94 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_name_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_name_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Name the NFC tag:"); + + set_random_name(app->name_buf, TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_name_text_input_callback, + app, + app->name_buf, + TEXT_INPUT_LEN, + true); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_name_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneResult); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_name_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c new file mode 100644 index 000000000..4e70bcb09 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_phone_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_phone_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Phone Number:"); + + strlcpy(app->text_buf, "+", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_phone_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_phone_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_phone_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c new file mode 100644 index 000000000..912bf3c9f --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c @@ -0,0 +1,359 @@ +#include "../nfc_maker.h" + +enum PopupEvent { + PopupEventExit, +}; + +static void nfc_maker_scene_result_popup_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, PopupEventExit); +} + +void nfc_maker_scene_result_on_enter(void* context) { + NfcMaker* app = context; + Popup* popup = app->popup; + bool success = false; + + FlipperFormat* file = flipper_format_file_alloc(furi_record_open(RECORD_STORAGE)); + FuriString* path = furi_string_alloc(); + furi_string_printf(path, NFC_APP_FOLDER "/%s" NFC_APP_EXTENSION, app->name_buf); + do { + if(!flipper_format_file_open_new(file, furi_string_get_cstr(path))) break; + + uint32_t pages = 42; + size_t size = pages * 4; + uint8_t* buf = malloc(size); + + if(!flipper_format_write_header_cstr(file, "Flipper NFC device", 3)) break; + if(!flipper_format_write_string_cstr(file, "Device type", "NTAG203")) break; + + // Serial number + buf[0] = 0x04; + furi_hal_random_fill_buf(&buf[1], 8); + uint8_t uid[7]; + memcpy(&uid[0], &buf[0], 3); + memcpy(&uid[3], &buf[4], 4); + + if(!flipper_format_write_hex(file, "UID", uid, sizeof(uid))) break; + if(!flipper_format_write_string_cstr(file, "ATQA", "00 44")) break; + if(!flipper_format_write_string_cstr(file, "SAK", "00")) break; + // TODO: Maybe randomize? + if(!flipper_format_write_string_cstr( + file, + "Signature", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")) + break; + if(!flipper_format_write_string_cstr(file, "Mifare version", "00 00 00 00 00 00 00 00")) + break; + + if(!flipper_format_write_string_cstr(file, "Counter 0", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 0", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 1", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 1", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 2", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 2", "00")) break; + if(!flipper_format_write_uint32(file, "Pages total", &pages, 1)) break; + + // Static data + buf[9] = 0x48; // Internal + buf[10] = 0x00; // Lock bytes + buf[11] = 0x00; // ... + buf[12] = 0xE1; // Capability container + buf[13] = 0x10; // ... + buf[14] = 0x12; // ... + buf[15] = 0x00; // ... + buf[16] = 0x01; // ... + buf[17] = 0x03; // ... + buf[18] = 0xA0; // ... + buf[19] = 0x10; // ... + buf[20] = 0x44; // ... + buf[21] = 0x03; // Message flags + + size_t msg_len = 0; + switch(scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)) { + case NfcMakerSceneBluetooth: { + msg_len = 0x2B; + + buf[23] = 0xD2; + buf[24] = 0x20; + buf[25] = 0x08; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x62; + buf[43] = 0x6C; + + buf[44] = 0x75; + buf[45] = 0x65; + buf[46] = 0x74; + buf[47] = 0x6F; + + buf[48] = 0x6F; + buf[49] = 0x74; + buf[50] = 0x68; + buf[51] = 0x2E; + + buf[52] = 0x65; + buf[53] = 0x70; + buf[54] = 0x2E; + buf[55] = 0x6F; + + buf[56] = 0x6F; + buf[57] = 0x62; + buf[58] = 0x08; + buf[59] = 0x00; + + memcpy(&buf[60], app->mac_buf, GAP_MAC_ADDR_SIZE); + break; + } + case NfcMakerSceneHttps: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x04; // Prepend "https://" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneMail: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x06; // Prepend "mailto:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerScenePhone: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x05; // Prepend "tel:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneText: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 7; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 3; + buf[26] = 0x54; + + buf[27] = 0x02; + buf[28] = 0x65; // e + buf[29] = 0x6E; // n + memcpy(&buf[30], app->text_buf, data_len); + break; + } + case NfcMakerSceneUrl: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x00; // No prepend + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneWifi: { + uint8_t ssid_len = strnlen(app->text_buf, WIFI_INPUT_LEN); + uint8_t pass_len = strnlen(app->pass_buf, WIFI_INPUT_LEN); + uint8_t data_len = ssid_len + pass_len; + msg_len = data_len + 73; + + buf[23] = 0xD2; + buf[24] = 0x17; + buf[25] = data_len + 47; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x77; + buf[43] = 0x66; + + buf[44] = 0x61; + buf[45] = 0x2E; + buf[46] = 0x77; + buf[47] = 0x73; + + buf[48] = 0x63; + buf[49] = 0x10; + buf[50] = 0x0E; + buf[51] = 0x00; + + buf[52] = data_len + 43; + buf[53] = 0x10; + buf[54] = 0x26; + buf[55] = 0x00; + + buf[56] = 0x01; + buf[57] = 0x01; + buf[58] = 0x10; + buf[59] = 0x45; + + buf[60] = 0x00; + buf[61] = ssid_len; + memcpy(&buf[62], app->text_buf, ssid_len); + size_t ssid = 62 + ssid_len; + buf[ssid + 0] = 0x10; + buf[ssid + 1] = 0x03; + + buf[ssid + 2] = 0x00; + buf[ssid + 3] = 0x02; + buf[ssid + 4] = 0x00; + buf[ssid + 5] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth); + + buf[ssid + 6] = 0x10; + buf[ssid + 7] = 0x0F; + buf[ssid + 8] = 0x00; + buf[ssid + 9] = 0x02; + + buf[ssid + 10] = 0x00; + buf[ssid + 11] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr); + buf[ssid + 12] = 0x10; + buf[ssid + 13] = 0x27; + + buf[ssid + 14] = 0x00; + buf[ssid + 15] = pass_len; + memcpy(&buf[ssid + 16], app->pass_buf, pass_len); + size_t pass = ssid + 16 + pass_len; + buf[pass + 0] = 0x10; + buf[pass + 1] = 0x20; + + buf[pass + 2] = 0x00; + buf[pass + 3] = 0x06; + buf[pass + 4] = 0xFF; + buf[pass + 5] = 0xFF; + + buf[pass + 6] = 0xFF; + buf[pass + 7] = 0xFF; + buf[pass + 8] = 0xFF; + buf[pass + 9] = 0xFF; + + break; + } + default: + break; + } + + // Message length and terminator + buf[22] = msg_len; + size_t msg_end = 23 + msg_len; + buf[msg_end] = 0xFE; + + // Padding + for(size_t i = msg_end + 1; i < size; i++) { + buf[i] = 0x00; + } + + char str[16]; + bool ok = true; + for(size_t page = 0; page < pages; page++) { + snprintf(str, sizeof(str), "Page %u", page); + if(!flipper_format_write_hex(file, str, &buf[page * 4], 4)) { + ok = false; + break; + } + } + if(!ok) break; + + free(buf); + success = true; + + } while(false); + furi_string_free(path); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + if(success) { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } else { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } + popup_set_timeout(popup, 1500); + popup_set_context(popup, app); + popup_set_callback(popup, nfc_maker_scene_result_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewPopup); +} + +bool nfc_maker_scene_result_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case PopupEventExit: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, NfcMakerSceneMenu); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_result_on_exit(void* context) { + NfcMaker* app = context; + popup_reset(app->popup); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c new file mode 100644 index 000000000..17d84e0a9 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_text_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_text_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Text Note:"); + + strlcpy(app->text_buf, "Lorem ipsum", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_text_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_text_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_text_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c new file mode 100644 index 000000000..e5a4996b2 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_url_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_url_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Plain URL:"); + + strlcpy(app->text_buf, "https://google.com", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_url_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_url_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_url_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c new file mode 100644 index 000000000..68efaf7f6 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c @@ -0,0 +1,55 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi SSID:"); + + strlcpy(app->text_buf, "Bill Wi the Science Fi", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_text_input_callback, + app, + app->text_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiAuth, WifiAuthenticationWpa2Personal); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiAuth); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c new file mode 100644 index 000000000..4cb90dabe --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c @@ -0,0 +1,83 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_auth_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_auth_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Authentication Type:"); + + submenu_add_item( + submenu, "Open", WifiAuthenticationOpen, nfc_maker_scene_wifi_auth_submenu_callback, app); + + submenu_add_item( + submenu, + "WPA 2 Personal", + WifiAuthenticationWpa2Personal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA 2 Enterprise", + WifiAuthenticationWpa2Enterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Personal", + WifiAuthenticationWpaPersonal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Enterprise", + WifiAuthenticationWpaEnterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "Shared", + WifiAuthenticationShared, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_auth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiAuth, event.event); + consumed = true; + if(event.event == WifiAuthenticationOpen) { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionNone); + strcpy(app->pass_buf, ""); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + } else { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionAes); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiEncr); + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_auth_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c new file mode 100644 index 000000000..d1a21f51a --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c @@ -0,0 +1,48 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_encr_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_encr_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Encryption Type:"); + + submenu_add_item( + submenu, "AES", WifiEncryptionAes, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "WEP", WifiEncryptionWep, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "TKIP", WifiEncryptionTkip, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "None", WifiEncryptionNone, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_encr_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiEncr, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiPass); + } + + return consumed; +} + +void nfc_maker_scene_wifi_encr_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c new file mode 100644 index 000000000..3f5b4bd76 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_pass_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_pass_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi Password:"); + + strlcpy(app->pass_buf, "244466666", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_pass_text_input_callback, + app, + app->pass_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_pass_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_pass_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +}