From cf2dbc1fc722177536003cf1f323fe5a99d0a0e4 Mon Sep 17 00:00:00 2001 From: RogueMaster Date: Sat, 26 Nov 2022 21:58:01 -0500 Subject: [PATCH] Two NEW FAPs --- ReadMe.md | 4 + applications/plugins/dice/application.fam | 2 +- applications/plugins/dice2/README.md | 8 + applications/plugins/dice2/application.fam | 13 + .../plugins/dice2/assets/ButtonBack_35x10.png | Bin 0 -> 220 bytes .../plugins/dice2/assets/ButtonDown_7x4.png | Bin 0 -> 102 bytes .../plugins/dice2/assets/ButtonExit_35x10.png | Bin 0 -> 211 bytes .../plugins/dice2/assets/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../plugins/dice2/assets/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../plugins/dice2/assets/ButtonRoll_35x10.png | Bin 0 -> 189 bytes .../plugins/dice2/assets/ButtonUp_7x4.png | Bin 0 -> 102 bytes .../plugins/dice2/assets/DiceCount_30x13.png | Bin 0 -> 182 bytes .../dice2/assets/DiceCount_30x13_One.png | Bin 0 -> 180 bytes applications/plugins/dice2/assets/coin_1.png | Bin 0 -> 539 bytes applications/plugins/dice2/assets/coin_2.png | Bin 0 -> 535 bytes applications/plugins/dice2/assets/coin_3.png | Bin 0 -> 204 bytes applications/plugins/dice2/assets/coin_4.png | Bin 0 -> 546 bytes applications/plugins/dice2/assets/coin_5.png | Bin 0 -> 530 bytes applications/plugins/dice2/assets/coin_6.png | Bin 0 -> 546 bytes applications/plugins/dice2/assets/coin_7.png | Bin 0 -> 535 bytes applications/plugins/dice2/assets/d100_1.png | Bin 0 -> 628 bytes applications/plugins/dice2/assets/d100_2.png | Bin 0 -> 618 bytes applications/plugins/dice2/assets/d100_3.png | Bin 0 -> 630 bytes applications/plugins/dice2/assets/d100_4.png | Bin 0 -> 635 bytes applications/plugins/dice2/assets/d4_1.png | Bin 0 -> 376 bytes applications/plugins/dice2/assets/d4_2.png | Bin 0 -> 490 bytes applications/plugins/dice2/assets/d4_3.png | Bin 0 -> 369 bytes applications/plugins/dice2/constants.h | 116 +++ applications/plugins/dice2/dice_app.c | 256 +++++++ applications/plugins/dice2/icon.png | Bin 0 -> 128 bytes applications/plugins/dice2/images/coin.pixil | 1 + applications/plugins/dice2/images/d100.pixil | 1 + .../plugins/vbmigrate/.github/FUNDING.yml | 1 + .../vbmigrate/.github/workflows/build-fap.yml | 12 + applications/plugins/vbmigrate/LICENSE | 674 ++++++++++++++++++ applications/plugins/vbmigrate/README.md | 121 ++++ .../plugins/vbmigrate/application.fam | 33 + .../vbmigrate/gui/modules/file_select.c | 481 +++++++++++++ .../vbmigrate/gui/modules/file_select.h | 31 + .../vbmigrate/icons/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../vbmigrate/icons/DolphinNice_96x59.png | Bin 0 -> 2459 bytes .../plugins/vbmigrate/icons/Error_18x18.png | Bin 0 -> 1083 bytes .../plugins/vbmigrate/icons/Touch_26x26.png | Bin 0 -> 14967 bytes .../vbmigrate/scenes/vb_migrate_scene.c | 48 ++ .../vbmigrate/scenes/vb_migrate_scene.h | 47 ++ .../vbmigrate/scenes/vb_migrate_scene_about.c | 75 ++ .../scenes/vb_migrate_scene_config.h | 33 + .../scenes/vb_migrate_scene_delete.c | 91 +++ .../scenes/vb_migrate_scene_delete_captures.c | 85 +++ ...vb_migrate_scene_delete_captures_success.c | 58 ++ .../scenes/vb_migrate_scene_delete_success.c | 59 ++ .../scenes/vb_migrate_scene_dev_menu.c | 107 +++ .../scenes/vb_migrate_scene_from_app.c | 373 ++++++++++ .../vbmigrate/scenes/vb_migrate_scene_info.c | 83 +++ .../vbmigrate/scenes/vb_migrate_scene_load.c | 61 ++ .../scenes/vb_migrate_scene_main_menu.c | 78 ++ .../scenes/vb_migrate_scene_register.c | 436 +++++++++++ .../scenes/vb_migrate_scene_register_save.c | 119 ++++ .../scenes/vb_migrate_scene_save_success.c | 65 ++ .../scenes/vb_migrate_scene_select.c | 57 ++ .../scenes/vb_migrate_scene_to_app.c | 291 ++++++++ applications/plugins/vbmigrate/vb_migrate.c | 319 +++++++++ applications/plugins/vbmigrate/vb_migrate.h | 31 + applications/plugins/vbmigrate/vb_migrate_i.h | 97 +++ applications/plugins/vbmigrate/vb_tag.c | 144 ++++ applications/plugins/vbmigrate/vb_tag.h | 80 +++ 66 files changed, 4590 insertions(+), 1 deletion(-) create mode 100644 applications/plugins/dice2/README.md create mode 100644 applications/plugins/dice2/application.fam create mode 100644 applications/plugins/dice2/assets/ButtonBack_35x10.png create mode 100644 applications/plugins/dice2/assets/ButtonDown_7x4.png create mode 100644 applications/plugins/dice2/assets/ButtonExit_35x10.png create mode 100644 applications/plugins/dice2/assets/ButtonLeft_4x7.png create mode 100644 applications/plugins/dice2/assets/ButtonRight_4x7.png create mode 100644 applications/plugins/dice2/assets/ButtonRoll_35x10.png create mode 100644 applications/plugins/dice2/assets/ButtonUp_7x4.png create mode 100644 applications/plugins/dice2/assets/DiceCount_30x13.png create mode 100644 applications/plugins/dice2/assets/DiceCount_30x13_One.png create mode 100644 applications/plugins/dice2/assets/coin_1.png create mode 100644 applications/plugins/dice2/assets/coin_2.png create mode 100644 applications/plugins/dice2/assets/coin_3.png create mode 100644 applications/plugins/dice2/assets/coin_4.png create mode 100644 applications/plugins/dice2/assets/coin_5.png create mode 100644 applications/plugins/dice2/assets/coin_6.png create mode 100644 applications/plugins/dice2/assets/coin_7.png create mode 100644 applications/plugins/dice2/assets/d100_1.png create mode 100644 applications/plugins/dice2/assets/d100_2.png create mode 100644 applications/plugins/dice2/assets/d100_3.png create mode 100644 applications/plugins/dice2/assets/d100_4.png create mode 100644 applications/plugins/dice2/assets/d4_1.png create mode 100644 applications/plugins/dice2/assets/d4_2.png create mode 100644 applications/plugins/dice2/assets/d4_3.png create mode 100644 applications/plugins/dice2/constants.h create mode 100644 applications/plugins/dice2/dice_app.c create mode 100644 applications/plugins/dice2/icon.png create mode 100644 applications/plugins/dice2/images/coin.pixil create mode 100644 applications/plugins/dice2/images/d100.pixil create mode 100644 applications/plugins/vbmigrate/.github/FUNDING.yml create mode 100644 applications/plugins/vbmigrate/.github/workflows/build-fap.yml create mode 100644 applications/plugins/vbmigrate/LICENSE create mode 100644 applications/plugins/vbmigrate/README.md create mode 100644 applications/plugins/vbmigrate/application.fam create mode 100644 applications/plugins/vbmigrate/gui/modules/file_select.c create mode 100644 applications/plugins/vbmigrate/gui/modules/file_select.h create mode 100644 applications/plugins/vbmigrate/icons/DolphinMafia_115x62.png create mode 100644 applications/plugins/vbmigrate/icons/DolphinNice_96x59.png create mode 100644 applications/plugins/vbmigrate/icons/Error_18x18.png create mode 100644 applications/plugins/vbmigrate/icons/Touch_26x26.png create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene.h create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_about.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_config.h create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures_success.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_success.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_dev_menu.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_from_app.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_info.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_load.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_main_menu.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_register.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_register_save.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_save_success.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_select.c create mode 100644 applications/plugins/vbmigrate/scenes/vb_migrate_scene_to_app.c create mode 100644 applications/plugins/vbmigrate/vb_migrate.c create mode 100644 applications/plugins/vbmigrate/vb_migrate.h create mode 100644 applications/plugins/vbmigrate/vb_migrate_i.h create mode 100644 applications/plugins/vbmigrate/vb_tag.c create mode 100644 applications/plugins/vbmigrate/vb_tag.h diff --git a/ReadMe.md b/ReadMe.md index a249605ac..20ce378ae 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -27,6 +27,8 @@ Thank you to all the supporters! - [Add protocol Auriol hg0601a #451 (By LY2NEO)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/451) - Added: [SubGhz: fix duration pricenton protocol #2054 (By Skorpionm)](https://github.com/flipperdevices/flipperzero-firmware/pull/2054) - Added: [POCSAG protocol decoder for subghz #2055 (By Shuma)](https://github.com/flipperdevices/flipperzero-firmware/pull/2055) +- Added: [Dice (By Ka3u6y6a)](https://github.com/Ka3u6y6a/flipper-zero-dice) +- Added: [VB Lab Migration Assistant (By GMMan)](https://github.com/GMMan/flipperzero-vb-migrate) `Req: Vital Bracelet` ## Install from Release FLASH STOCK FIRST BEFORE UPDATING TO CUSTOM FIRMWARE! @@ -163,6 +165,7 @@ $ ./fbt resources icons dolphin_ext - [BlackJack (By teeebor)](https://github.com/teeebor/flipper_games) - [Chess (By Okalachev)](https://github.com/okalachev/flipperzero-firmware/tree/chess) Crashes 1st load if FW <~750KB or every load on larger FW `Broken?` - [Dice Roller Including SEX/WAR/8BALL/WEED DICE (By RogueMaster)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/applications/dice/dice.c) +- [Dice (By Ka3u6y6a)](https://github.com/Ka3u6y6a/flipper-zero-dice) - [Doom (By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) - [Flappy Bird (By DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird) [Flappy: Border hitboxes, bigger Pilars (By TQMatvey)](https://github.com/DarkFlippers/unleashed-firmware/pull/114) [Increase pilars line width to improve visibility (By ahumeniy)](https://github.com/DarkFlippers/unleashed-firmware/pull/140) - [Game of Life (Updated to work by tgxn) (By itsyourbedtime)](https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c) @@ -239,6 +242,7 @@ $ ./fbt resources icons dolphin_ext - [UART Echo (By DrZlo13)-OFW](https://github.com/flipperdevices/flipperzero-firmware/pull/831) - [USB HID Autofire (By pbek)](https://github.com/pbek/usb_hid_autofire) - [USB Keyboard (By huuck)](https://github.com/huuck/FlipperZeroUSBKeyboard) +- [VB Lab Migration Assistant (By GMMan)](https://github.com/GMMan/flipperzero-vb-migrate) `Req: Vital Bracelet` - [WAV Player (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) Updated by Atmanos & RogueMaster To Work - [WiFi (Deauther) V2 (By Timmotools)](https://github.com/Timmotools/flipperzero_esp8266_deautherv2) `Req: ESP8266` - [WiFi (Marauder) (By 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) `Req: ESP32 WITH MARAUDER FLASHED` diff --git a/applications/plugins/dice/application.fam b/applications/plugins/dice/application.fam index 83cd02032..4cf6506b7 100644 --- a/applications/plugins/dice/application.fam +++ b/applications/plugins/dice/application.fam @@ -1,6 +1,6 @@ App( appid="Dice", - name="Dice", + name="Dice [RM]", apptype=FlipperAppType.EXTERNAL, entry_point="dice_app", cdefines=["APP_DICE"], diff --git a/applications/plugins/dice2/README.md b/applications/plugins/dice2/README.md new file mode 100644 index 000000000..89955d4d9 --- /dev/null +++ b/applications/plugins/dice2/README.md @@ -0,0 +1,8 @@ +# Flipper Zero Dice App + +## Compiling + +1. Clone the [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) repository or another firmware that you use. +2. Create a symbolic link in `applications_user` named **dice**, pointing to this repository. +3. Compile by command `./fbt fap_dice_app` +4. Copy `build/f7-firmware-D/.extapps/dice_app.fap` to **apps/Tools** on the SD card or by [qFlipper](https://flipperzero.one/update) app. diff --git a/applications/plugins/dice2/application.fam b/applications/plugins/dice2/application.fam new file mode 100644 index 000000000..81be4349f --- /dev/null +++ b/applications/plugins/dice2/application.fam @@ -0,0 +1,13 @@ +App( + appid="Dice_app", + name="Dice [Ka3u6y6a]", + apptype=FlipperAppType.EXTERNAL, + entry_point="dice_tool_app", + cdefines=["APP_DICE"], + requires=["gui"], + stack_size=1 * 1024, + order=90, + fap_icon="icon.png", + fap_category="Games", + fap_icon_assets="assets", +) \ No newline at end of file diff --git a/applications/plugins/dice2/assets/ButtonBack_35x10.png b/applications/plugins/dice2/assets/ButtonBack_35x10.png new file mode 100644 index 0000000000000000000000000000000000000000..2c22d19c65501ac0a8bec5369491fd20409a93bb GIT binary patch literal 220 zcmV<203-j2P)Y;00001b5ch_0Itp) z=>Px#mPtfGR5(w~lu-_VFbG58{)e8~NR?oz`!VX*G8nv;IXAOmAAe&6g1-raCQ7UO zL7pJ3;%0Zq2L!A?b6-JZXjIF!93fwt#gSouoY8e>LJV7%EHjUO9 zX6sb2Gd7G)IfGg`N5bU0qSSY>>~<9=VRpyVsjhdfb2g8*_WaGSz1)5Co!Na$;o_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/ButtonExit_35x10.png b/applications/plugins/dice2/assets/ButtonExit_35x10.png new file mode 100644 index 0000000000000000000000000000000000000000..22f35791382668794bd6d9892677057109ad1fcd GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^%0SG;!3HGls-7o978Pp z&rW&B*Py`B^6;iu?pk;w>p0J?ZoHS!L{CoN0~R2Py2cKSV14qVGN$G KelF{r5}E*@*j5Mt literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/ButtonLeft_4x7.png b/applications/plugins/dice2/assets/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/ButtonRight_4x7.png b/applications/plugins/dice2/assets/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/ButtonRoll_35x10.png b/applications/plugins/dice2/assets/ButtonRoll_35x10.png new file mode 100644 index 0000000000000000000000000000000000000000..f20d7f5651b2232ec6609199cf8ea77f86b78b5f GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^%0SG;!3HGls-7_kj978Pp z?@qbRbwGi~`S$<+FZy~|HwCmBwi(4=sd|~2eP-3Fj*c@f+qbADa1<5#HtuV2vpRQ1 z;K)IP@)LY_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/DiceCount_30x13.png b/applications/plugins/dice2/assets/DiceCount_30x13.png new file mode 100644 index 0000000000000000000000000000000000000000..a408de0253f5e9d6b8eb9f19960a2abba06b1ebb GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^av(Mb8<5;rbZ$OxrnOa1_>y{BCSXDH?uF})JQS8$Xt*l) z=I#Z@qi^1|VgIn?7k^3mW$m?{t-23<4y@WE^JnT-4t;j{|J_;rOS@F%)_=A77U}u& gRm82GB71)^bv;gg&)TQO0koUJ)78&qol`;+05<7LEdT%j literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/DiceCount_30x13_One.png b/applications/plugins/dice2/assets/DiceCount_30x13_One.png new file mode 100644 index 0000000000000000000000000000000000000000..ec61bde963d9d278e27a82f8095a26c891c6eb4a GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^av(Mb8<5;rbZ500001b5ch_0Itp) z=>Px$)k#D_R9Hu?m`iemFbqWF`yZMV)R+?O{>aQ^lLf|pYPBUX<2a7vZ}V@A(RyQd z>(PZlfOOFA`ZKx+Q#VBbrI1_ag0L|2u(|;Qn9tp_EAmfz6_A9%7o9@NK6uBNU^=Qy2zMEnEhvm_W~C0{m3>4B>s+dCA(%1=1|!ERJLPAvHh^xL-ZV-F z(@s;3EP1Hg8eA)oX_|A#a_z>jeXIi|EAPi&tvbVc5A#I=dJThzb<2OJHK>$PWiVCj zwtS-u21Y%zV_cQG1j$snE3ukqraG#oyiv=6T<56~VrSax_(}StPL^)c>i~TV@~{RQ z$mvK}K54ttAgkJLZlZ;(`h+SRE2QERQ~V!F?)}gg|=Xrhf<%ZRlGZX#Ggc1ajO%Z>hj&9S=DXBsEV6X dmAqBo{|8=Fy|#JGK~Dex002ovPDHLkV1g(E@5TTC literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_2.png b/applications/plugins/dice2/assets/coin_2.png new file mode 100644 index 0000000000000000000000000000000000000000..08c5872d92409cf7cefac8980d590943eaa35a58 GIT binary patch literal 535 zcmV+y0_gpTP)500001b5ch_0Itp) z=>Px$(Md!>R9Hvtmq`wTFbqY5Kp^1$R|PRwL3QOgo}gWH0jn0U^YT4~;y8{Y{K-{3 zFhV45DN_`MJFr)>zzstIJd0qP`2IZ4p{{FjBPI-Wh&^cvVOV(2+JFkfInm%aPyq1Q zi7-`Fg>Bmcb;j+EQwZC6RbpYNBJsZ@vGff4z`yr>4`o@NB2u3jOf-y1HciuMa0i5j zi&LrZ`!Ebc0>r{lW3CD~NGUJk{bgA~3_Z4^J`xxzEjZw6QB#b*pDp?hdzKD(9DtFv zkfjR1NJ)&tpC|<|R28i!SV>EhQ-Y6cBW@UvGKs|Z$Nnj~VoD-~s z)#7i#NJ$0vxh7?WM|v)mw_4g+heSsQoP1Y6DG0U|VXz|a9cCc~N1nXNdJ5UtxgI@s zVxx6k&#q~O%7W4QQZhs(7%54u>cR=jH9wk1Dh#MFj$>%s_8epsAP&){X_EWeB3yB> zYDr%QT&ZKIIJc||24g%jz}blAPBP)OPZc_=@P7-0hS@Xs+-G127dQOGzJU&9KY^hM z`3;QKk4$A~IJj!kl))7hPj+3`i4ndG!DNScOMw>obyi! Z`~nUKLbkrCS4;o^002ovPDHLkV1is#;U)k8 literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_3.png b/applications/plugins/dice2/assets/coin_3.png new file mode 100644 index 0000000000000000000000000000000000000000..c757caa6695c5312daed96171c726a2722044a5d GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^${@_a1|;QfZjJ;}jKx9jP7LeL$-D$|+B{txLn7SY zPJ7GMpuppL`?vj5Q@DM$uO*UeC}z!)y<95re0zpUXO@geCwn C98iV; literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_4.png b/applications/plugins/dice2/assets/coin_4.png new file mode 100644 index 0000000000000000000000000000000000000000..508184d14b3aa917e5fc8421c6ba671d05a6b8e3 GIT binary patch literal 546 zcmV+-0^R+IP)500001b5ch_0Itp) z=>Px$+(|@1R9HvtSJ{q(FbqsMis1i$6%Yu-%BYFFj<2kE=t}JqDj^<^Cu1jG*L8&t z`Nc07p%VX_nHZy~u)oSsWoB8HFilgy08$|MPkK&hYeA`C5(p0f4VV{{2qtB&G2jJB zlalJ}2IJ8R4gx@FLO3xF5DNxp0?%#RhVwjcjU2~e3b^n4yoQ2;7gE^@FrI8QmLSl! zZDAOOLiq6Ax~^dyM;6d8FkBF<3w%oGT4`PSM%z2D!Rz&t)gy@yM@0K*+uOm&eB%ANw1fG1HGiKa41eqsT^;I!bll$?jb`CdMebwI+Jj_TL?!W#$$ST41rkQIGe~ z0BEt;qwP7OTyV)8)Bj+4(6`K^O*h9IB-fNYhIVVp9Q3N!>y_D;SS)%IOm_1;-}`wP zGTS)W3!3P@??tm@yEbeNBSX_PavwosXRThOF~Xc@BnNhNVbUn!8<>rxnMj@VJk3pV zTCTxLjO2u^8ztLamFtQoa?3K*+X09yodU{|CR%CmvV>lBqvUiV1+Z9PjnOKTmCcjy ktC)E*@RykyHb1INKOyjAwkz_P#sB~S07*qoM6N<$g37({+W-In literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_5.png b/applications/plugins/dice2/assets/coin_5.png new file mode 100644 index 0000000000000000000000000000000000000000..85831d239d67638a09a6aa78757bc2d3b2d160f7 GIT binary patch literal 530 zcmV+t0`2{YP)500001b5ch_0Itp) z=>Px$%t=H+R9HvNnCq^CFbsp&_kU<6(1KFePFyriJHL*gmoKrKhK=Jmj@Rbj7{i*e zyYz5j5TM&=cl;S$3zHW`0F|L?ToptK6Px7)7(l$Q-d&M@#aPf0f^h|e>vTW6Q`Q37wGOaT24v5NL1h}Y> zGgGA;qsSYfft9zIoYS5RhWVy4FCOhKd#J!YKVZ~=?%1*d*TF>1Imb==W*FyGD0z0~ zW|#>KVLB7DOg%Z?Oo_YB#DcqWsVFOX7kNIlio$#&KRq6u=#R}UCu;TIJbX=$WZO4M|5!M#8ow_g;6I%)farujI6S1!!RgEs-0^62F0Q0 UwhRtf7XSbN07*qoM6N<$f^nYiB>(^b literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_6.png b/applications/plugins/dice2/assets/coin_6.png new file mode 100644 index 0000000000000000000000000000000000000000..17cdbf1057ef6a657ec5fc8331ea661d991fd844 GIT binary patch literal 546 zcmV+-0^R+IP)500001b5ch_0Itp) z=>Px$+(|@1R9HvtSKF?HFbJ&o=>Pw!8jZ#iOlpu06y29S$?n^l7KUM9v^b9A2w(Cm zeqfYN{9k5@qHwG0jTB90)^!bS+XfE8lSG2{+@UJ7ZHj?;K#L)n_(>ru7!(8nlYP^Y zyY8aV*4W6gqp=d7Q{1Ym!nSQtdkHfU$iDBPEXzwT-oB$O6q5s@Gbr8-3|$(AA&ldA z#^gcJ|Cm@4tOOAObX^x{I1(m#o6u#Vw65y}m;}Nrm+f4Z<(f+E+-;h;PM$I%lOyfW zGb*fUnv)Tz%g9UwAc1IG&eIy}ZUl5IgurO5c*ap`Nh=2Bknv9eTVOm>qzM)6AkN5R zFxnXML>@37Gvjqd4;Ta?M>Zf=2@rs;h1({>PgQ9@_Et<8R_XuHEo;Bk6%|1lVEm2b}d kz`PRh8>&CK^Hpv71J|8gwlxgUJpcdz07*qoM6N<$g1OoHh5!Hn literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/coin_7.png b/applications/plugins/dice2/assets/coin_7.png new file mode 100644 index 0000000000000000000000000000000000000000..82f828a94e8d920e8959415a8d09bc69c8b18698 GIT binary patch literal 535 zcmV+y0_gpTP)500001b5ch_0Itp) z=>Px$(Md!>R9HvtSKF$CKnz@|DC+wAczP63z)^03xI(T zF*rpg7~Cj*yM`eP04R?k?N`A_j8MPK^X%d&%klv6dcD%Ru7|I<+%$*?1~v@Ckea5s z1b~eYa2&@}*Yz1#LdCtIv222oN|pxJs>z~>`iZZ*#OC9G(zq;30;=Jo(W@C!Gd_Zm z;$38t%9r+uoBK(B(U_s~pv*M6%QMQ1m(4|A0f;0a9vtaCH)d!VT0XG)g~y=UuaKEf zvcjd6fJ^e7%)ARm^8uHM8kqN;Fc`1ob0vMmjAlxnq1TSNR3;cor)DrN1ue^Wz?_w4 zl{U;DoG&>COTeTAZ+%Vbz`Q3+)0C>JQfE=*Mu*+pWL*LS2g5M5DuI@7wCB)uUDq8q zKXW!obI3a^@*ZX0w(Yo5NV2Q<8So0;L99Xf#JHOO+?yWX=#@Gxd2cY@#)kKg(mGsf zgnSg_u500001b5ch_0Itp) z=>Px%E=fc|R9HvNSV3}xFbrGZ|4#-wX4Z^QBqyOgluKD4wqnb&)8#zR^Yc%xZgHa2WmhdyRb6154Hf&(Z?{sEwQo@!ciaSO_juXf)<(Tdj8bi-;u1`y87_iYf!UW_ zvrHR`H;>4;2Ie?~m4uBldlZ9Y`%H%>Oq$V=Hd-zYx87cBY;D8Vqv8rJ)kco*+t{8Cm3jji%$zA~OSa_S+xkI3LA2M6nhC O0000500001b5ch_0Itp) z=>Px%BuPX;R9HvVm`jp_Knz1^xc`xXl&P2!LjFKDGug}(0rzWLmYa-y-}mW{e8w*@ zV+^^CxBGhxg8;TD1M#&dU~L$Nu$0&}Ukv~Ebonz~pcV`URi*K4sH{8zDq%R7XF&Z$ z0bw?}Kq(BDVO`g0S(ckWx!Rf16hEV4 zGSCgC3Ty$C9{jq}m@kT1c|Kc$SupB z8D(1Aw%xkN0q1$XX^^{5i8K;+nHdab5DpL-pK|3#nX}BG=qwwyDC$RnWTsY)2G-`O zHH5($vcq!CrB948Ie2H5v11?Il=(`hW$~V>oU*0A1NQ=he&B$(Q`Nd^WVlO5W*H7! zl^Jg7N|?4Nb{@~L%zrLbQJS&9xVs3(caH6D!FfjE^WGq+ms6h+3=hN1<}EM+sDa7a zV@VtvL(@|olIaNV_cHNhSGq}C8{0NgJMYYe%`V-A;kK3z9EnX$a}|)`s-wik+p4L} zM(Fx*m&WfAB%48zfws;wN_=Q;_Pqk}=c4ae60MUS!bE49O8l%#smN1ITYeEi;o?#x3mJC?GGzk-1n@c;k- literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/d100_3.png b/applications/plugins/dice2/assets/d100_3.png new file mode 100644 index 0000000000000000000000000000000000000000..92d5a5c0c5c5241af4365a4b989614ef4206525f GIT binary patch literal 630 zcmV-+0*U>JP)500001b5ch_0Itp) z=>Px%FiAu~R9HvNnA>uLFbqTk{QoBd88MM(y}XHIAPCo6m91)CERPD( zeGD+s=(5A=cZ}G|x9@vv?&ch(@X0)i9_j^WM>V1XkHp;8B9xl21~VgE(PS}b1mq3w zWgC2YX0lpx^QsAn01y;m5UANxFiftL6vzI$F3DnJj1mB|g`I#gl?>au}?s4{~_ zXU5$d(49ud3RG$ae7-SEv=MSNnd;g%bT+F|4ThtM`iS?T=vaYrz_t}=%Z&e_5-%nD z)%0OzvMddOMWUBY80v=-M2uVso$8mz4#My>sl9~Xsj~?;J|n4|II66oN@oyA zfpCz~xhCDUBO@q=ymO=cBe>Eq?7bl~JOOARQ&M(RodEcIQ+RcWQzt#&Z`8h`GF6@a zWUoaxN>zIVnsJt$e`MLR>uod@s!up`m546Gu!dlBa+>)#p)#jR)9r&Z7k?%B*?Cqq za0Mc#Nu^KiGbrV%;nB_v$P$bySp@zK$TCb<-{5~UmC$7Ylo$V*=>KZX8+sof#rCpI QzW@LL07*qoM6N<$f(29@$N&HU literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/d100_4.png b/applications/plugins/dice2/assets/d100_4.png new file mode 100644 index 0000000000000000000000000000000000000000..324b7f6332929d6efd21a50495339da417ff9a37 GIT binary patch literal 635 zcmV->0)+jEP)500001b5ch_0Itp) z=>Px%HAzH4R9HvNSV3~aKn!c(|4)W=oIxE$k?e&YT23S(yRvM_UbcPT_pg8Q***oc zZJQ0@H?h41Mgi2ue9v9KL;4b!wBh6N_`2P0EuaFk%GL;`fFR<;M1i{B?=fvyV`8ZV z!;82YVUV@a^Z7hL6}b8oYxTjq6GIVex zPB_J!qQuYzj8lxVGUYyjVbBiH3CyZ$cu^<{08ux%?&Pn4ESQvgrJaFrO)wy=l|1US z7aW)uo4%ufRV`W%alE9L(l2l~XnwU{~wN45sJOvE7x^YTV0sQP$ zU^)r9l(FMF_w54V6ki0xU>I@HjlQFc13=xc-+?73U3GDG=Lkj{S}Hz72IqCnlGjRL z>U{-&NIUf%c3UFqQl-RnnV@9LZc7R#|)vi6bG;5c) zHRGb_`iR!SRBy^6g5kmJ*sn9au9h4i*O|3fITlYoGcglQ>NN%GbCx?l)251jFJMx^ zO^wb)>UxxGA(z&}M-_EqJ9E5_C`r<*K(v?-GW6WkncZabZuDR&yYCFX9XqE{qEp;d zhnDqpqI4;9!xeiCt503pjSO0EOGgzud(iPVjS1XPRXNrur?a*jj=B+&*>g44sc$?{ zzT0D*n$)2apd~O0=%fHLzU0cTSU*aWZp=oN(xqlL86%!AGjpl@zy5z_HNsDS_YH2W VAH{?w500001b5ch_0Itp) z=>Px$GD$>1R9Hvtm*EP+Fbst4`#*H0U?4kl=_Q*WWB!sk*UvB4q>ee~eErF5JTRmZ zj~N!x`jIsEu|=;k)Pl(jc(AIWHcVtCyQVbxGK{yv$`j@3 zXD~`u7Em?RB{Pu~p9}W6y35Ki-bxfuGqTEp@7J{~)H2M@N|XsSvNKeI*;!#gK09_# zXjM7SbgbB-*mu$%rNS^PN|TGq)iRS=QOCfgFp-rAB+bKT!drgIW)v&_;h6z^RtpBZ zZPtC!41@Fi$L~P8wRby6DHH26tofxd%u2LvSzgdWd26%DjCJ#L_kDY?A>0000500001b5ch_0Itp) z=>Px$q)9|UR9Hvdm%)<5APhs7|NrQ8va@82kZhS;I@?R9gu;uElyq4oECl zrVLX@jV)`%-sH;Zz+^zmKZ`Q4G_g!ocqwJ({EpHOOcv?bi?DR>-x<6Mw7{^^R80}* zR)JStU)lkNSKKUS3U+6vNTww7+jFGTz*SW+&#>dcB(7S?QvL{|GGxL`08C&sEW$IN z#l5y;V%;~}Br3xa8E-&&@)>t>hBZUgn<~GsW1}!uN|FaDR47vH(om0KUshnU2W_RpBY@x?Uv8+2isRPl1 zQB_7w7~V3SM@`!^%0pP5(KA_aX6i2qTuoFJ5y!hqY&TmOSV2}v+qt>6XHFLH{-=D2 g>n`|a{#U_#00xg@wqrW?(f|Me07*qoM6N<$f=ag6aR2}S literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/assets/d4_3.png b/applications/plugins/dice2/assets/d4_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1687ac805aac3fd89ec99dbcdc3abf9ea41a55 GIT binary patch literal 369 zcmV-%0gnEOP)500001b5ch_0Itp) z=>Px$D@jB_R9HvtSJ4i{AP77E|D#=8&V(ZrsIto*+~a1Xwxzh%d+(o@Jneu1N*rd8 zMYox0t|NaDUpGf{CS zc}u!UPgx!5l0DR2@Df!qwWHk0f}%a+rDz6}239f-sz5yshB3Nis#GS=n4Zzg?laLO z&UBw-k{XmuafT^(;;;T4FkrnqQ8*}>LX~T`e&Q;D+=>1VnX;e{7{t^CzDJ6ujA> +#include "dice_app_icons.h" + +#define TAG "DiceApp" + +#define DICE_TYPES 8 + +#define MAX_DICE_COUNT 10 +#define MAX_COIN_FRAMES 9 +#define MAX_DICE_FRAMES 4 + +#define DICE_X 45 +#define DICE_Y 6 +#define DICE_Y_T 0 + +#define DICE_GAP 44 + +#define SWIPE_DIST 11 + +const Icon* coin_heads_start[] = {&I_coin_1, &I_coin_2}; +const Icon* coin_heads_end[] = {&I_coin_7, &I_coin_1}; +const Icon* coin_tails_start[] = {&I_coin_5, &I_coin_6}; +const Icon* coin_tails_end[] = {&I_coin_4, &I_coin_5}; +const Icon* coin_frames[] = { + &I_coin_1, + &I_coin_2, + &I_coin_3, + &I_coin_4, + &I_coin_5, + &I_coin_6, + &I_coin_3, + &I_coin_7, + &I_coin_1, +}; + +const Icon* dice_frames[] = { + &I_d4_1, &I_d4_2, &I_d4_3, &I_d4_1, // d4 + &I_coin_1, &I_d4_2, &I_d4_3, &I_d4_1, // d6 + &I_d4_2, &I_d4_2, &I_d4_3, &I_d4_1, // d8 + &I_coin_1, &I_d4_2, &I_d4_3, &I_d4_1, // d10 + &I_d4_1, &I_d4_2, &I_d4_3, &I_d4_1, // d12 + &I_coin_7, &I_d4_2, &I_d4_3, &I_d4_1, // d20 + &I_d100_1, &I_d100_2, &I_d100_3, &I_d100_4, // d100 +}; + +typedef struct { + uint8_t type; + int x; + int y; + char* name; +} Dice; + +const uint8_t screen_pos[] = {}; + +static const Dice dice_types[] = { + {2, 0, 0, "Coin"}, + {4, 0, 0, "d4"}, + {6, 0, 0, "d6"}, + {8, 0, 0, "d8"}, + {10, 0, 0, "d10"}, + {12, 0, 0, "d12"}, + {20, 0, 0, "d20"}, + {100, 0, 0, "d100"}, +}; + +typedef enum { EventTypeTick, EventTypeKey } EventType; +typedef enum { SelectState, SwipeLeftState, SwipeRightState, AnimState, ResultState } AppState; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + AppState app_state; + uint16_t roll_result; + uint8_t rolled_dices[MAX_DICE_COUNT]; + uint8_t anim_frame; + uint8_t dice_index; + uint8_t dice_count; + Dice dices[DICE_TYPES]; +} State; + +void init(State* const state) { + state->app_state = SelectState; + state->roll_result = 0; + state->dice_index = 0; + state->anim_frame = 0; + state->dice_count = 1; + + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i] = dice_types[i]; + state->dices[i].x = DICE_X + (i * DICE_GAP); + state->dices[i].y = i == 0 ? DICE_Y_T : DICE_Y; + } +} + +void coin_set_start(uint16_t type) { + if(type == 1) { + coin_frames[0] = coin_heads_start[0]; + coin_frames[1] = coin_heads_start[1]; + } else { + coin_frames[0] = coin_tails_start[0]; + coin_frames[1] = coin_tails_start[1]; + } +} + +void coin_set_end(uint16_t type) { + if(type == 1) { + coin_frames[MAX_COIN_FRAMES - 2] = coin_heads_end[0]; + coin_frames[MAX_COIN_FRAMES - 1] = coin_heads_end[1]; + } else { + coin_frames[MAX_COIN_FRAMES - 2] = coin_tails_end[0]; + coin_frames[MAX_COIN_FRAMES - 1] = coin_tails_end[1]; + } +} \ No newline at end of file diff --git a/applications/plugins/dice2/dice_app.c b/applications/plugins/dice2/dice_app.c new file mode 100644 index 000000000..ab2c72257 --- /dev/null +++ b/applications/plugins/dice2/dice_app.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include "constants.h" + +const Icon* draw_dice_frame; + +static void update(State* const state) { + if(state->app_state == SwipeLeftState) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i].x -= SWIPE_DIST; + state->dices[i].y = DICE_Y; + } + + if(state->dices[state->dice_index].x == DICE_X) { + state->app_state = SelectState; + state->dices[state->dice_index].y = DICE_Y_T; + } + + } else if(state->app_state == SwipeRightState) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i].x += SWIPE_DIST; + state->dices[i].y = DICE_Y; + } + + if(state->dices[state->dice_index].x == DICE_X) { + state->app_state = SelectState; + state->dices[state->dice_index].y = DICE_Y_T; + } + } else if(state->app_state == AnimState) { + state->anim_frame += 1; + + if(state->dice_index == 0) { + if(state->anim_frame == 3) coin_set_start(state->roll_result); // change coin anim + + if(state->anim_frame >= MAX_COIN_FRAMES) { + state->anim_frame = 0; + state->app_state = ResultState; + } + } else { + if(state->anim_frame >= MAX_DICE_FRAMES) { + state->anim_frame = 0; + state->app_state = ResultState; + } + } + } +} + +static void roll(State* const state) { + state->roll_result = 0; + + for(uint8_t i = 0; i < MAX_DICE_COUNT; i++) { + if(i < state->dice_count) { + state->rolled_dices[i] = (rand() % dice_types[state->dice_index].type) + 1; + state->roll_result += state->rolled_dices[i]; + } else { + state->rolled_dices[i] = 0; + } + } + + if(state->dice_index == 0) coin_set_end(state->roll_result); // change coin anim + + state->app_state = AnimState; +} + +static void draw_ui(const State* state, Canvas* canvas) { + canvas_set_font(canvas, FontSecondary); + + FuriString* count = furi_string_alloc(); + furi_string_printf(count, "%01d", state->dice_count); + + // dice name and arrows + if(state->app_state != SwipeLeftState && state->app_state != SwipeRightState) { + canvas_draw_str_aligned( + canvas, 63, 50, AlignCenter, AlignBottom, dice_types[state->dice_index].name); + + if(state->dice_index > 0) canvas_draw_icon(canvas, 45, 44, &I_ButtonLeft_4x7); + if(state->dice_index < DICE_TYPES - 1) + canvas_draw_icon(canvas, 78, 44, &I_ButtonRight_4x7); + } + + // dice settings + if(state->dice_index == 0) + canvas_draw_icon(canvas, 48, 51, &I_DiceCount_30x13_One); + else + canvas_draw_icon(canvas, 48, 51, &I_DiceCount_30x13); + canvas_draw_str_aligned(canvas, 58, 61, AlignCenter, AlignBottom, furi_string_get_cstr(count)); + + // buttons + canvas_draw_icon(canvas, 92, 54, &I_ButtonRoll_35x10); + canvas_draw_icon(canvas, 0, 54, &I_ButtonExit_35x10); + + furi_string_free(count); +} + +static void draw_dice(const State* state, Canvas* canvas) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + if(state->dices[i].x > 128 || state->dices[i].x < -35) continue; + + if(i == state->dice_index) { // draw dice with animation + if(i == 0) { // coin + draw_dice_frame = coin_frames[state->anim_frame]; + } else { // dices + draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES + state->anim_frame]; + } + } else { // draw first dice frame + if(i == 0) { // coin + draw_dice_frame = coin_frames[0]; + } else { // dices + draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES]; + } + } + + canvas_draw_icon(canvas, state->dices[i].x, state->dices[i].y, draw_dice_frame); + } +} + +static void draw_results(const State* state, Canvas* canvas) { + if(state->app_state != ResultState) return; + + canvas_set_font(canvas, FontPrimary); + + FuriString* sum = furi_string_alloc(); + furi_string_printf(sum, "%01d", state->roll_result); + + canvas_draw_str_aligned(canvas, 38, 44, AlignCenter, AlignCenter, furi_string_get_cstr(sum)); + + furi_string_free(sum); +} + +static void draw_callback(Canvas* canvas, void* ctx) { + const State* state = acquire_mutex((ValueMutex*)ctx, 25); + if(state == NULL) { + return; + } + + canvas_clear(canvas); + + draw_ui(state, canvas); + draw_dice(state, canvas); + draw_results(state, canvas); + + release_mutex((ValueMutex*)ctx, state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t dice_tool_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + + FURI_LOG_E(TAG, ">>> Started...\r\n"); + State* state = malloc(sizeof(State)); + init(state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, state, sizeof(State))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(state); + return 255; + } + + // Set callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() * 0.2); + + // Create GUI, register view port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + AppEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + State* state = (State*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // timer evetn + if(event.type == EventTypeTick) { + update(state); + } + // button events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + // lock input while animations + if(state->app_state == SelectState || state->app_state == ResultState) { + // input + if(event.input.key == InputKeyUp) { + if(state->dice_index != 0) { + state->dice_count += 1; + if(state->dice_count > MAX_DICE_COUNT) { + state->dice_count = MAX_DICE_COUNT; + } + } + } else if(event.input.key == InputKeyDown) { + state->dice_count -= 1; + if(state->dice_count < 1) { + state->dice_count = 1; + } + } else if(event.input.key == InputKeyRight) { + if(state->dice_index < DICE_TYPES - 1) { + state->dice_index += 1; + state->app_state = SwipeLeftState; + } + } else if(event.input.key == InputKeyLeft) { + if(state->dice_index > 0) { + state->dice_index -= 1; + state->app_state = SwipeRightState; + if(state->dice_index == 0) state->dice_count = 1; + } + } else if(event.input.key == InputKeyOk) { + roll(state); + } + } + // quit from app + if(event.input.key == InputKeyBack) { + processing = false; + } + } + } + } else { + FURI_LOG_D(TAG, "osMessageQueue: event timeout"); + } + + view_port_update(view_port); + release_mutex(&state_mutex, state); + } + + // Clear + free(state); + furi_timer_free(timer); + furi_message_queue_free(event_queue); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/dice2/icon.png b/applications/plugins/dice2/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..840088565327533035cafbde39119b257c4b3cb5 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>vGR0r4ABTq z{`3F;|9Unh24x>bWo6~Crv}9bxE#9*BzYz?OEPbljbyfxc2(3{-ekhNq|@d1tQ!R^ Z45x&>KgkKWOaYq0;OXk;vd$@?2>{vvBaZ+8 literal 0 HcmV?d00001 diff --git a/applications/plugins/dice2/images/coin.pixil b/applications/plugins/dice2/images/coin.pixil new file mode 100644 index 000000000..838aeafc0 --- /dev/null +++ b/applications/plugins/dice2/images/coin.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAFlJREFUWEft1MEJACAMBEGv/6IvFeRxhoDC+hYMm0HZ9nnkiGGaTVCmI0oZyqTfF2Ywg5m0AGbSYvwzYzOS1iuuP5C4YZixmST37V3WxJpSO5jBDGbSAl+YKXbCj5ghLqGvAAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":"1","active":true,"unqid":"zdzpbp","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAf5JREFUWEftVkFywjAMVAZooP//apukScYdq7ariJXsAAcO4UICtrxerVbq6I0+3RthoQOMlY1nmAkNKd4Vf9fidLgEkffH36znjDn+/0lE369gJoPYHBpCCF3XUQiBuvhA8fH/WYGMOEwCWpiRIO4Y2AmGwVqAamDyRvTdIJmyRIJ4CAxipOhlnucqmMvlwiBOpxOt65pTpNO9QYyChmEY6Ha76QD8Po5juF6v+baterRYdsGgkpXptKrJY0qKXq8rsZFm+LBYEcuyUKKaf0pR9B5LA1D4XGqRXi48/phgSoBcJeIaFvA9RcCHW7HRLbNnuJ5glChKYWYV+pNkx6K8UJ9u8SgrmdicHdcYkTBdYxItAbKq1IlaRAGY9FKk8UowUuQ6xUjkstT/NC1uYtm+vIlZll5dO/qSXtUExhKlpruC564nucxI1VsjgU5FreIkQJ0q7ch37XyTqth/zuczNKgaDeB/dMGNkdZKlgNM0xT6vvd6Uc34aJ7nkBunMYjBQScC+CGivrGM9UDlkSbZ+UqTn9ubpC48n/D0oAGVdKzryiOFdF74oiIsRBR36Y7L744zIyCWhWzW1nKNOnWt1LUvofEBnlsDA11VsIJmHwYjhnKUdqirFjCWA7fs1Sx+pOJ4GowWbKvVtIDmWM0LW09+Zt0BxmLvYMZi5hdntuIk2wS1xwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2 Copy","opacity":"1","active":true,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAmlJREFUWEelWOt6qDAIs+//0O5rV1hIA9Rz9mdOsYQQLm48//bzXr42Lu2W2RfjAOB9azxjHEe3vlqD53ncKwMQDleEjV3qswOzgODhCoA9r56tNPyxJf1WYAIQdETgjjPebaDeqQBlYCSQTieZWA2AYDD4V2CugIxMMBuRsWMAbwBJMFUE00kHxAAIWxWoY2AwboyRoPMEjKrzwbb7b6841s8BBlmBa7cjB6razNYAhnetqoj9ZSPBcH73AYrFdQ/1IVK4Hs/7XGX7vuNAByFFXfnuZhjAFOmcZzsYZAdTFcB8SNE6HIVKTc2jBZ0wcG+mG1AYIEZnaOlJtAGMpUpVWQXGunsLxmg1B5TvtD9R47OKYqbm+1y5mhkUXAYm6SHmxH+DnevGxDwZQREfmgGD0CfUvGHdUFN2jeyADp1dgdlUc9PCxtZNe88WMwNpvEsTCMtzyxpCbVQjgsBgb8O0/RYjIk1KW4nPq/rrtlj5uAGD7PCWkI2JbJvwZm16gf6UM2OpglFw9BZuesnYWOPCRAzV0zc9c0DIefh5Y8SZA++mS5uBEatsZIbYCA6RBSjVLB1c1nbWZIjP9eme7jOQSw84afu8y1Q6yvbqeoXgbU98fvDED4HdrJ3VCrEChx7DAuNPFtTR/wDx1iEHXqF4zzeWJDdALALQ2rokzTiQcAEHhi9IrCqhIwUuiJqrBgPdhk5INWPCfoOVpiIndgIg/piz/YVLsQSDFPNnUvdBp+zVVEdAN9PXGRIUZ33G72OTyxgJvaM5UWqIZ0s2ACuNfEkT26bCroDBIW0WWgPB2u1/rb6wv2x/ACDUeEL6Qq7OAAAAAElFTkSuQmCC","edit":false,"name":"Background","opacity":"1","active":false,"unqid":"u6oba5","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfZJREFUWEftVl1vgzAMNCoM2P//qxtQiDw5JZkxdj5YH6qpeULCdu7OziUNvNBqXggL/AswWKFoMeHiQACQAEpyeQ7FfwDA3SJypSAgIjZNQ7mmQogIewztHeKS++XAaEUkW4uozM3mpcBsAHADiEN+KM7U4cw5sFCb8vg+pkoWGI1VKlb7x2vEb+cc3m7E0a9DnlWEhqzfEyQzzt7/I5V85cccycXz+fcXAHxyQEXJy7Jg3/cyVgNpzQWu6wpd14WhV1uobRDk8wXati1hnLMdbW74afQ4LLap1oSBtYiUDDGvEfeypvwKGHmqZO0U8cfIMRpy+muY17ZJKlMMJmdWORUD0OzclChjmZbqFQmJqsCoQ7U7be3GZ7M5epG3Jna/maeJLkIKtE4bB5ZroSZUNErmkcnNvKsKQ9VaGj1JsQnNiX28Vdu8U0idbduCa/Jjm3NtUw1f5Lddp9anLjjNvFJeYinB2629fyIG8yaepgnGceRtiEzmecZhGLida2rk3P1kCRYY2ZaQGJnRvZVb4WKkJ4NzThI77Z0Cox31E6gcoP0/v61Nk8yBsRSKQMPJ4L6hecj+DpZPiAOXEjAhQX39VYLRvCsCqgEjQclToj2YpCnSy+7bau0VMBqop9T/C5jC2S0Pe4N5Sk/LBb8W+W6TpdsPAVbkJHCu0MYAAAAASUVORK5CYII=","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAKFJREFUWEftVVsKwCAMa+9/6I4Oiw/cap2Cg+zHQWuaJXEyHfTwQVwIZJ7cgDJQJnpSkRlkBpmJKjCSGVkFOonD5dEWkcyHuaq5+Lo5sqcclkb1yViRiJSdEq7WNJiMvPa/vVu9Xe0Lt5BJytzES8KfyCjjiOSuh50G16YZ0IV7YiFdOLgLhYty5Ke32wUXHzbBJjckTQMyg8wgM1EFfpGZCziRWCSDveGOAAAAAElFTkSuQmCC","edit":false,"name":"Layer 4","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":5,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfhJREFUWEftl91uwjAMhR21HbC9/6sOOkCZ0tWVe+q/VprEBVyh4iZfju3jUOiFPuWFWOgNY2XjiDI1kdoj64Zp+iSibyKSALxRe2Z9Z95dUFEwQ6w2rbXWUqZHhb/XWqn8PUTIKS6hphvEi24U2AnDgCGUR4wwmVqRAkiVtINtxLJgZHpq13X0fD6nk93v9xBqGAYPylRIg9EUwTgPqNxut3o+n2V6GKBer1e6XC4qUGYTWbyySxDIiuN3tAOs9ldhWmdM6HPLQIdkGgMBpn1aivu+b+uqrS9hFnJoU5RUdpd2atd7vLU3MIZfyE2t1KLHtHdU8HmPjToIs5gYG5pIl6XIZg3Io/fe6jcsOu10Vo1EIwLfW9nF7Morh49gcBxoYNaM8mKlfSw1mYGJ5koW5uWUwUZQ0yQrXys4z1v2KINtrxYwwyy2DWPfAsTnZlxr58fjQfPcUie55hkr+uaawzBYXRZafPOacRzpdDrhGhtwDabd7L7mvIQnb3cbGB2eFfB+IxF94KXLmtrTlaHrOlXOLKigwrpKTW11wDmbowrs4JplcF22i1Gvyed5iOWw6qlABW0qhwYaGRp2GYn7r1UbljrRIcJbeyuyH+OvSuQ98vfMoUMYa9h5IFqKMvG7YVKLHg1KyXd08b3vvWHMNtwr5X/G/wLO/uskz2jgOQAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2 Copy Copy","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":6,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAkBJREFUWEetl1t2wyAMRO39L9o9pkgZDSMJ2vQrTQRcRk/u629/z+aye9NumJ0YB4DnqXnue9m6Pas1uK7LT2UAceC4YWOXntnBDBDcXAHY79Vvww0fteS5FUwAwYMIbtnjmQZqTQWUwUiQBKKNJQMQCobzFUwLMiW3ta/98tnUsWzaAZIwzQ3w8J3MZViPwQnoDAzjquBN7o+jx4Xh/xLmNZ62A2j+7xnH8bPAoCrw+bXDDXGdKjit4qT+sJcwiX+34ysrppxlU2XnWG5oBl36QjEc8kNQZwq+seIqvhcGmN/l4HS3NUPKmrEZxg8GDMRSCHBa40B0hoahwPXMnJUYYQKLSm+xJkuQGmZu7rckV3AWyVqTXMBsGWx1E/jSM8g2RbcJl6kiyK4dW2GaYxAvMVPBFPXlWBmrOS3M9EFQpim1xzCWjVswnHbfDGCMR6jKZ6mdqGO1Q8bMEukwXnAVPqozGzAMtizBKrwNgxNe0xjb3oSZx80SJkDtJjNQjTILZCr1WR/zzi1G2QhDamCr93j48ghhGbtUYCtIPPyElmBFq0n1qlWo/esRgqc98fz499hZjRBBGdFV+cmCMKiEjxQu6YwGHty6Sc/bUDFAo8zpMwWyxGMPv2NVxm/C9+EFiQrxAbb25BFHlT0wZO+mRSHMNJZfVNnwFT/m+FXAGaKSY1EopMjhw79SZAcGvDCeJzyzbmU3ztSZIkcwGBsIlTxrQh3ZUeQExlkUlAV1BgbyVfE5zFqDKtu2/HRwxg9BIkRCZQ/enQAAAABJRU5ErkJggg==","edit":false,"name":"Background Copy","opacity":"1","active":false,"unqid":"u6oba5","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":7,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfpJREFUWEftV9tugzAMdQQM2P7/VwcUUKakJHKMHZuqk3hon6piHx8f36iDG33cjbjAh4xUjavK+AtlvYqtlukLAB4AgElYglDSFh+VTBAiAUdA7713rortDptkZE6khsqBFMSEkknBVV+JDOcYfsP2XP9IqmCVdgBouUQ4MjHIvu/QNE14rmZkaGqciIhHyQTDXwD4OQJwahQ+Qg9hJbjvCwCE4SiwODKF87quvuu6pJCoJAI+JbAsC/R9TzGoHdsDXOCTI1IufKW9dErSew/btkHXdcn+VC4ORApMW0PaJRb/ZFPY0l3ASUkz5/qVNqhWTkwm41vIWLanhUxaoDjholQWMrdRxrBCoolVmYRXLVMGRHsjbtMoy/MWWbc118DSvmIbOB/EEN89o6sLT9jOml/MER3bnCR7awghrIrlPnEq5jVQwT5JH52O6iSFaE9Y++j0+hG2edu2rCrVDFBEOnFUqWRas8PEcDLV2yTtAu6GSQq5eZ79MAz44ucTME0TjOPIDoRlQnzTNPGVIkxUkFqr03F/sGLsKFOc2nal90MlQcDppq1d/uh6hYy4i8KYCivBpAhtOkl5euYjOF2MChkt6RxbO4Lfx5sfLlHx8nUg0XNgTbYQQSPDKWbpnVdwTf+btOF52/OXMnhbdGb8/gv7Mu5HGUmyP5KE6CQNV8vwAAAAAElFTkSuQmCC","edit":false,"name":"Layer 2 Copy","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":1,"unqid":"rs6rne","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAdFJREFUWEftV9uqg0AMXKko6v//quL9ECESY25r+1AO7UuL7mYnM5NkW+z7vqcv+RQ/MIoS/4OZoihcp+XaMZsZCgIPg2fab0QM7/u+T23bqkmEwSCICAALmMWWC4aCsBiIgqQxOEUmGNwofbuGIQsAKI8h7VfBSIxQv8zz7OIpy/IA8Xq90rquh6+43DSICAY2DMOQmqa5BICNEHAcx1TX9RlYQsW9obFsgpFKlgbWqsmiifqJr7vE5uOA0rgsSwKq4aPRqxlSMz6CkeJdZJLKl/aJm/tJf9GYofKgzDS5yzPKjJfNRV8BiCQhHhzpPTdmaBlaPrD6hceg2q+QmU+ywqWVgEvyncy8C0bzAZeJGpj3nTAYr+RzJX3EjGZKnqHXjrlUJhhKp2YwLgUty3fAnJVmlTbMH5wvOQdLwKQEb31N6sC8J0zTlKqqUmdR5EaH3dxkXQIDB8MwtCqBlm+059B1XdcdNz9zNklzyDss8h5l3rbtuFJIsqv3GfAK3kGsQfnEuJr/3Jse3+iVOi15bzDexob3j9I6XGqEfBTksOpeyHmm0mGaVDwRrEp1vceM1jM8r+SAPhN+AiYKJHddWKbcwE/W/8B81MBPJIjs+QPyMEG2lHD1VgAAAABJRU5ErkJggg==","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAdFJREFUWEftV9uqg0AMXKko6v//quL9ECESY25r+1AO7UuL7mYnM5NkW+z7vqcv+RQ/MIoS/4OZoihcp+XaMZsZCgIPg2fab0QM7/u+T23bqkmEwSCICAALmMWWC4aCsBiIgqQxOEUmGNwofbuGIQsAKI8h7VfBSIxQv8zz7OIpy/IA8Xq90rquh6+43DSICAY2DMOQmqa5BICNEHAcx1TX9RlYQsW9obFsgpFKlgbWqsmiifqJr7vE5uOA0rgsSwKq4aPRqxlSMz6CkeJdZJLKl/aJm/tJf9GYofKgzDS5yzPKjJfNRV8BiCQhHhzpPTdmaBlaPrD6hceg2q+QmU+ywqWVgEvyncy8C0bzAZeJGpj3nTAYr+RzJX3EjGZKnqHXjrlUJhhKp2YwLgUty3fAnJVmlTbMH5wvOQdLwKQEb31N6sC8J0zTlKqqUmdR5EaH3dxkXQIDB8MwtCqBlm+059B1XdcdNz9zNklzyDss8h5l3rbtuFJIsqv3GfAK3kGsQfnEuJr/3Jse3+iVOi15bzDexob3j9I6XGqEfBTksOpeyHmm0mGaVDwRrEp1vceM1jM8r+SAPhN+AiYKJHddWKbcwE/W/8B81MBPJIjs+QPyMEG2lHD1VgAAAABJRU5ErkJggg==","palette_id":false} \ No newline at end of file diff --git a/applications/plugins/dice2/images/d100.pixil b/applications/plugins/dice2/images/d100.pixil new file mode 100644 index 000000000..c4fd81342 --- /dev/null +++ b/applications/plugins/dice2/images/d100.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAExJREFUWEft1LERACAMAzGy/9DOBCncUYgJuI/OkyTvkzc+c1xCmYuoMsq088UMM8y0BZhpi9kZZphpCzDTFrMzzDDTFmCmLWZnrmIL7qWLmCXUhAcAAAAASUVORK5CYII=","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"1exvb","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAArBJREFUWEfNWMF2wjAMa+n/fzJ0zyEyiiKn5W2H9bIBia3IsmLYt3/07L/AclZ7z/Pc9n3/Ova3G0oAfwHsFpjzPE86aOwJUNXfCVcwFc8VW5dgCEhL3kuwPZ/P7TgOAAKAnYFTudq+K0BLMMJICwYwCA4Ur9crGTmOA+sGsFeASjDmhAMYrUUwFU8AYdDu/4ohC6YCIgmx14oaDAI0v64YmsCoRhBMT87MxGePxwOaSGY6A7bRHKABjDLCUUITKEG8zwA0GzqPEvKS1JECUmYa5UoxRWqBHBMCaEro6AnQbJAJRsoTJ2/AiA13omzpfgjnRxOOSj/MzCREMMDObvylectQh89NwK2dgu8xcgvYUTCDsVHJMqgEGkyuR9+DVTDqEqv+BjBVBzkwRPGqtRNQrOfkWiLopnVePw3fNYOA2dJF3G4v31dtuTM9B67lITAAl/V3rfktM647XbdOYLjNIMrFbZ3gsQ9akWSsq9Sk1Y0wY93SeAhKkA0AK8DaLuAGRBOrbyV7WiZ2YXVZYSk1geDuPlo5d3wW10iaHwvYjQycKB3u7SOtY4SJ9hL3WPzPV0gkD8D83nCQThGcc3BZN8aKqBsqLZEBmNdIBYRbmzWghopxseoynfZUd5VhDoNajKTTdVC1c0NOy7Vj4CmrDmA3Nub3iV4MVGXZKq+owMRB3NzMY4Sb9PTCTG9gdpTBYnZJQaNryPUh1/zWMA1XSOjatCqVXhkFO9b8esx3e+pGdl62/qu273py44QttU55Fkxy1wVr6J8GMoCGQJkp3o91DkgJRgERe0MbayItY99X7Zmq4gScuauS8TdLPm23CvfVt8WsGEHCJZgFQww475aCmVtAlmViYbMl6xWh5uU6Lk9+8TPJJTMVqDss3AVxu0yFZ7x/jlg8Vz9/uK0/fGymIjAHw4EAAAAASUVORK5CYII=","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAArJJREFUWEe1mFt2wjAMRDHsf8khPVItdTwaOaGn5QuSWL4ePcN4/PJznue5WzrGGJ+a/nhBbGAw3X7G+e8wAsAOwwot1z4Bu6VMA2EiGciA+/E97AYoX5cevIQhEKWEGfbrU4XcCH9HiO3ct4VBEJA7N4XNCiTGd8TWFVALI6R/HMfxeL1eRWKliMsFCRXP7IAkDMdIGDIY+yAQGF9glWpXQAVGuMaCIU+pygu6QX2PmPKIn7aUQguMAkED4B9cl6lN7sIsi6XLNQZiZTxVsXaQay6zCWPljopYh+QJ4xg3QZaA5mAOZSmg81CoDsO4jGH9/X6X7AnA5/OZEFA7cvmmNQXIAmQ2tsrgkQ3CAOYa1SSz6mKGiVLb1qRSto/jOOemWVkDREidfXN+KUAZuT/nLtU64efDeNKl10SZ34Dg4c9ZGEuzjPWdah5rBHPZY6gxIoivBZhopLJiLwvH+K4/QpmlyEGs+OFU48S+1WWg6lWLpJ0yaJBkVYXMIS3Wok1wD0NYTHWuSUoZN2wPmnGEEf5O19jzEfgIw4eBBMkyYkkt3WQXub5wb1LpjQAM0/Qr9xL2KldmN8+CXzNDKEh9fRyAO7tqshPuXtHjngK1sQBhrBj4FYxSNWKqVOAu2BQQ5azHwA0XZx2MhlxgcLLjiZ8Dl9I9U55AVM3CwdyTpGuUS+xw9+Xf2CLMqAKJzfBeY9c9VIarkvuzp4CRbqYplTYusOvx+tyvwkRmIRAZjAO0L26mAicA1quwje7BIConiqEEg5ZH0jQAU4ga2EH9ZTxFRbYwSqE4iZ08qjOPFhwPVKPQrHwX5xl4UQkV2myUryiboM/MUopcKgOx4vLyGCnglmapwHYgJZu6dMC5WL0liiBPUzQ6bD2xvclw/AfR7v8ZXHv3v5qPYNpC8kc3vgArjLsiNA/itgAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAqNJREFUWEell9uSgzAMQ6H9/0+m3bEbZ2RFduiWl90Bkhxk+dLz+Of1fr/f3dLzPM9vt/5qAQNU5zHnXbBbMAjBAHZw3AsIfAfBdlBbmADpVLBnCgTDBM/LM1sYBAEFbI0/Cgj468+O4+C/zrUDKmEKReIQ3rxTplqznC1hWBE7GcI019h7qM51Xcfz+XSw4Y9pN1Zx7JnOX2Cq0MTBKl0xhCLjpjKF2SdDgilAlpAEEPkmhUNAn/BB03eokAoTFzNeKOG6bLLwPR6PCDWeGYng95b4h+uHR6TE9M4UoSvKtt/wVGnoREnptxzCxYxM3bYH2ywMzh8ThmeYFJKgicxAr0CGbb0yak+CQaAEw8aqwiCU84+5rkuqYmk+LgdGZWR1Hje9au7SF5RyYRACDnYF4sL7r9fLaxDeS5kJMH4AAkUWEAQaPymCdSRUNAAGtcyyy55FlvlagkH1qv9xzfQch2oA+McxEGycsxVhoIx/OuHaBrAJhkKzVhgQtIMpqCp2DKiUKRthiu1niOtUdT9FeFT45FyEBo4U5B6CqU2jhFtDjAwJlPdDVVJLGUbz7gubzrBiBw5TwrsBw/6ayth6dTjuNVsJ7DK/sFOGaoUqBylEXFsYDqDSBL8ULmpwzi36iwOhNEWbmKFTgGYF1ShTVeUqGl8iMs0z8E6/quYaHiH8C9n9XBewGBaDeqofZcEag3yIsgxX/LMjNuoq6eLeIXgx4yygEqbLrOg3qq8opbphi0I9BVGTXoo9hoyA2lbSFbrqJ4uEIYWShyo/sXKiPHg0KxAs6dJjaqTAiomLzFNx8c+VuN+BbGGUhwJGzbM8gmD13oHcguGQFX2Iu3lS+g7IbRhqGVXZqEIdvy6360oDb1d+uvXPALjBLzA3eL975Q81aKYi/zu6FwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAp5JREFUWEe9l8uWgzAMQ0v7/59M6bEbe2RFDtDFdDPTB/hGVuSwPX58HcdxrC7dtm27e+vLF3DxrhYz3oE6hUEIBrDC+Jl6H+pcgVrCBEhXcN/3x+v1sntkyxjIYEKtM6AWhkC8YKfEgJIWiWuuAEkYAeIL5Gpc6P1+m1IPg7PX8/n0v6HsGdAEo1pj9xsgCRQFrXi80LwIMNrj166ACgyC8I2jOBgyVz5gWTnZ2hVQwjSKuMr23Y+xUXyGnlMKFZjVrrH+w/e5g0yx8IaRh2+wdd19B1wy+D9ClVAji8KqJhD2BAKBAux/bKNzJIxaNcuKKwxFEISqlYXgrhKtb2HSo+gVBFM7KQJw3/cDdtgEhBsA4NfKsLwdDCnk9++AusGZtSI/uhinvIhiHm6jcP6PK+1UjWtoQ3j+ZJjhVrMbDROmybDnEf92jf3OXgNO+gRVhhbPMw2TVSWoyhmcRWpYRrCFuRuY2ECZzFPMr6I9kpYMjKHIUZDK8naXxxH0DBpskbi+ADBpkZtVgfd5e5VZxTMIgrI2obURjNooPBizLZC89Rw0yHP2dNRCNW4xDt0CAi3ittW2MgwFkTzDwM5qPRfG51nVZM3pOJi8wOYevirzzX5D2z3r81CFUVJhMEfwfNINOhWSi1UfaoRQFzL0fHLbStWZQ+0yguxU9M8bkLimHiHCN6hO6/hvb/6Cqj6rTSbmrS1OAjMMA1EbJMAdFZXq/OiCK/Fu8DmYzzk8vW0mKV9ha9hffMILG0wwJ0CycGdmHKg4p7qHOQnTeShXMD/TZ5hRO8phvlNkqQzkiRtVnWnImMvdBL9tFx+zoomH78djy2OxDDkhvcFPqXymyCVlkFI8Oyk1ymdXIW7DnIDl13cBSqAue/TPX34AQUDBIiqR4IkAAAAASUVORK5CYII=","edit":false,"name":"Layer 4","opacity":"1","active":true,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":1,"unqid":"vr50fd","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjVJREFUWEfNWEFywkAMa+D/T4Z0nEEdRZHshR5aTiQhu7JsyV62fd/3r3/y2T4Fs22bDeE3sb0FRgHUxnzPXb9D+hKYtOHj8fi63W4toAKzytYIpoAg4sREgbrf7zFtWGNiqQXDi7gaYZC10fP5PEAVuPoUa/XBuxNDEYymhhet79iQGeHNFMAKIAtGaa1rbA6q6x4i1/ShTjjFnKLE0AWMU4yyMuU+gWHQDtAJzKpqeFEoCgBRN7ju5K+ALJiUe6WdgXBNMCCslTzqtBccmBebvqOAVS1aF1MRX54rGKZXZYmXnZLAkrKSVGTVmphRerVOCig8BQy5unFKUwH87FVgOgm6lLHjOvftAuEUXwTDYBhx0Y2+o4sDQN2v34ElJ111cXbnCEYbWio+RAbndc2S10pg6n0Ff0g79R12WzYyLmBmrWMmFfbJj1KaHECOGIunVpCU2LqzY0aj1XaAWgHVrj24NGuXtzXjouj6iNbBVG/KIoLhwezYrzO9rpYcINeZtVc5Fk8+MzETTeo1Bepzljs/06aKfS9gUsFNjc5F6nqUayGX1sNHFXViO3O8jihJ0qtAtJgPMvTc1JndZPNu9GApu+8Mvh07p1GiU9xUh5b1dKJ0KXPDFXsNq09d2o0MWm9LRxU7e9DxNo0J3FBVOdYop7O2G466EZLZczXWqW88Ub7bY5ya1KEToCUwCuikADK+5MquWD9Kk27MRpXSpWY2GSOeLzNzqfy//n9mNcJPf/cNbqwfxYQm5T8AAAAASUVORK5CYII=","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjVJREFUWEfNWEFywkAMa+D/T4Z0nEEdRZHshR5aTiQhu7JsyV62fd/3r3/y2T4Fs22bDeE3sb0FRgHUxnzPXb9D+hKYtOHj8fi63W4toAKzytYIpoAg4sREgbrf7zFtWGNiqQXDi7gaYZC10fP5PEAVuPoUa/XBuxNDEYymhhet79iQGeHNFMAKIAtGaa1rbA6q6x4i1/ShTjjFnKLE0AWMU4yyMuU+gWHQDtAJzKpqeFEoCgBRN7ju5K+ALJiUe6WdgXBNMCCslTzqtBccmBebvqOAVS1aF1MRX54rGKZXZYmXnZLAkrKSVGTVmphRerVOCig8BQy5unFKUwH87FVgOgm6lLHjOvftAuEUXwTDYBhx0Y2+o4sDQN2v34ElJ111cXbnCEYbWio+RAbndc2S10pg6n0Ff0g79R12WzYyLmBmrWMmFfbJj1KaHECOGIunVpCU2LqzY0aj1XaAWgHVrj24NGuXtzXjouj6iNbBVG/KIoLhwezYrzO9rpYcINeZtVc5Fk8+MzETTeo1Bepzljs/06aKfS9gUsFNjc5F6nqUayGX1sNHFXViO3O8jihJ0qtAtJgPMvTc1JndZPNu9GApu+8Mvh07p1GiU9xUh5b1dKJ0KXPDFXsNq09d2o0MWm9LRxU7e9DxNo0J3FBVOdYop7O2G466EZLZczXWqW88Ub7bY5ya1KEToCUwCuikADK+5MquWD9Kk27MRpXSpWY2GSOeLzNzqfy//n9mNcJPf/cNbqwfxYQm5T8AAAAASUVORK5CYII=","palette_id":false} \ No newline at end of file diff --git a/applications/plugins/vbmigrate/.github/FUNDING.yml b/applications/plugins/vbmigrate/.github/FUNDING.yml new file mode 100644 index 000000000..2cabf7401 --- /dev/null +++ b/applications/plugins/vbmigrate/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: caralynx diff --git a/applications/plugins/vbmigrate/.github/workflows/build-fap.yml b/applications/plugins/vbmigrate/.github/workflows/build-fap.yml new file mode 100644 index 000000000..f65b8609a --- /dev/null +++ b/applications/plugins/vbmigrate/.github/workflows/build-fap.yml @@ -0,0 +1,12 @@ +name: Build FAP +on: [push] + +jobs: + build: + if: ${{ false }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build + uses: oleksiikutuzov/flipperzero-ufbt-action@v1 diff --git a/applications/plugins/vbmigrate/LICENSE b/applications/plugins/vbmigrate/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/plugins/vbmigrate/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/plugins/vbmigrate/README.md b/applications/plugins/vbmigrate/README.md new file mode 100644 index 000000000..7238768f0 --- /dev/null +++ b/applications/plugins/vbmigrate/README.md @@ -0,0 +1,121 @@ +VB Lab Migration Assistant for Flipper Zero +=========================================== + +This app is designed to make transferring your characters from VB Lab and VBC +Lab more convenient. + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C81P4PX) + +Background +---------- +The Vital Bracelet Arena app is going to be the new companion app for the Vital +Bracelet series of fitness bracelet toys, however the app does not support +account linking from VB Lab and VBC Lab, and requires you to transfer characters +one by one from those apps through your Vital Bracelet. Because the Vital +Bracelet can only hold at most two characters at a time and requires inserting +Dim/VBM to complete the transfer, transferring all of your characters may take +a significant amount of time. The VB Lab Migration Assistant is designed to make +this process faster by allowing you to store an unlimited amount of characters +on the Flipper and to bypass the Dim loading process, therefore allowing you to +transfer your characters without having to flip back and forth between the apps +or wasting time loading data that will never be used. + +Usage +----- + +### 1. Register Vital Bracelet + +Registering your Vital Bracelet to the Flipper creates template data used for +emulating the VB using the Flipper. + +1. Select "Register Vital Bracelet" from Migration Assistant's main menu. +2. If you have an active character on your Vital Bracelet, **transfer it to VB + Lab**. The active character slot on the VB has to be empty to avoid issues + when transferring character from VB Lab to the Flipper. Press the right key + on Flipper. +3. If there is an active character in VB Lab, put it in storage. +4. On VB Lab's "Scan" page, tap "Vital Bracelet -> App". Press the right key on + Flipper. +5. Tap the Flipper to your Vital Bracelet. You do not have to put the VB in + scan mode. When Flipper finishes reading, it will beep. +6. On VB Lab, tap "Send", and tap the Flipper to your phone. VB Lab will give + you an error message. This is normal, and you can dismiss it. The Flipper + will beep. Wait for it to switch to the next screen. +7. Tap the Flipper to the original Vital Bracelet again. The Flipper will beep + when it's finished reading. +8. Name the Vital Bracelet and select "Save". + +The Vital Bracelet info will show after saving. + +### 2. Select Vital Bracelet, Vital Bracelet Info + +Use the "Select Vital Bracelet" menu to load a previously registered VB. After +loading, you will see information about this Vital Bracelet: + +- Registered name +- Vital Bracelet type +- Number of characters captured + +Press the right key to see a menu of options you can use with the registered VB. + +### Spoof Version + +Vital Bracelet Arena splits Vital Bracelet and Vital Hero Digimon into +mutually-exclusive categories, and currently transferring VBM characters from +VBDM has not been tested, so if you want to transfer between categories, you +can select the Vital Bracelet version to spoof to change the VB type and force +the app to perform transfers between categories. + +### 3. Transfer App > Flipper + +Use this mode to transfer characters from VB Lab to Flipper. + +1. An instructions screen is displayed. Press right key to continue. +2. On your VB Lab app, wake up the character you want to send (if you have not + already). +3. Go to the "Scan" screen, and tap "App -> Vital Bracelet". Select "Check Dim + Card". If prompted to send Special Missions or items, tap "No". +4. Tap the Flipper to your phone. Release when VB Lab indicates to do so, and + wait for Flipper to beep. +5. Tap "Check Dim Installation" on VB Lab, and tap Flipper to your phone. + Flipper will beep. +6. Tap "Send" on VB Lab, and tap Flipper to your phone. Hold it there until VB + Lab shows the transfer animation. Flipper will beep after it has received + the character. +7. Tap "OK" on VB Lab. Flipper will have automatically reloaded the template so + you can repeat from step 2 again to transfer more characters. +8. When you are done transferring characters, press the left key on Flipper to + exit the transfer mode. + +Note if you have previously transferred characters to Flipper, the new +characters you transfer will be put at the end of the sequence of characters +when transferring from Flipper to app. + +### 4. Transfer Flipper > App + +Use this mode to transfer characters from Flipper to VB Lab. + +1. An instructions screen is displayed. Press right key to continue. +2. On your VB Lab app, if you have an active character, save the character into + storage. +3. Go to the "Scan" screen, and tap "Vital Bracelet -> App". +4. Tap "Send". Tap your Flipper to your phone, and hold until VB Lab shows the + transfer animation. Flipper will beep when it detected the transfer and will + load the next character. +5. Repeat from step 2 until all of your characters have been transferred. +6. If you want to skip a certain character, press the right key to skip and load + the next character. +7. You will see the "Transfers complete" screen on Flipper when all characters + have been transferred. Press the right key to return to the menu. + +### 5. Clear Captures + +If you are doing transfers in batches, you can clear the currently captured +characters so you do not have to skip them when transferring to app. Check the +details, and press the right key to confirm deletion. + +### 6. Delete Vital Bracelet + +If you want to unregister the Vital Bracelet, you can use this option to delete +its captures and remove it from the "Select Vital Bracelet" list. Check the +details, and press the right key to confirm deletion. diff --git a/applications/plugins/vbmigrate/application.fam b/applications/plugins/vbmigrate/application.fam new file mode 100644 index 000000000..02871f81e --- /dev/null +++ b/applications/plugins/vbmigrate/application.fam @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# VB Lab Migration Assistant for Flipper Zero +# Copyright (C) 2022 cyanic +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +App( + appid="vb_migrate", + name="VB Migration Assistant", + apptype=FlipperAppType.EXTERNAL, + entry_point="vb_migrate_app", + requires=["gui", "storage"], + stack_size=2 * 1024, + fap_version=(0,2), + fap_libs=["nfc", "misc", "mbedtls", "ST25RFAL002", "gcc", "toolbox"], + fap_category="Tools", + fap_description="Makes transferring characters with VB Lab less cumbersome", + fap_author="cyanic", + fap_weburl="https://github.com/GMMan/flipperzero-vb-migrate", + fap_icon_assets="icons", +) diff --git a/applications/plugins/vbmigrate/gui/modules/file_select.c b/applications/plugins/vbmigrate/gui/modules/file_select.c new file mode 100644 index 000000000..c7560af75 --- /dev/null +++ b/applications/plugins/vbmigrate/gui/modules/file_select.c @@ -0,0 +1,481 @@ +#include "file_select.h" +#include +#include + +#define FILENAME_COUNT 4 + +struct FileSelect { + // public + View* view; + Storage* fs_api; + const char* path; + const char* extension; + + bool init_completed; + + FileSelectCallback callback; + void* context; + + char* buffer; + uint8_t buffer_size; +}; + +typedef struct { + FuriString* filename[FILENAME_COUNT]; + uint8_t position; + + uint16_t first_file_index; + uint16_t file_count; + +} FileSelectModel; + +bool file_select_fill_strings(FileSelect* file_select); +bool file_select_fill_count(FileSelect* file_select); +static bool file_select_init_inner(FileSelect* file_select); + +static void file_select_draw_callback(Canvas* canvas, void* _model) { + FileSelectModel* model = _model; + + FuriString* string_buff; + const uint8_t item_height = 16; + const uint8_t item_width = 123; + const uint8_t text_max_width = 115; + + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + if(model->file_count) { + for(uint8_t i = 0; i < MIN(FILENAME_COUNT, model->file_count); i++) { + if(i == model->position) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, (i * item_height) + 1, item_width, item_height - 2); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, (i * item_height) + 1); + canvas_draw_dot(canvas, 0, (i * item_height) + item_height - 2); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + 1); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + item_height - 2); + } else { + canvas_set_color(canvas, ColorBlack); + } + + string_buff = furi_string_alloc_set(model->filename[i]); + elements_string_fit_width(canvas, string_buff, text_max_width); + canvas_draw_str( + canvas, 6, (i * item_height) + item_height - 4, furi_string_get_cstr(string_buff)); + + furi_string_free(string_buff); + } + } else { + canvas_draw_str(canvas, 6, item_height, "Empty folder"); + } + elements_scrollbar(canvas, model->first_file_index + model->position, model->file_count); +} + +static bool file_select_input_callback(InputEvent* event, void* context) { + FileSelect* file_select = (FileSelect*)context; + bool consumed = false; + + if((event->type == InputTypeShort) | (event->type == InputTypeRepeat)) { + if(!file_select->init_completed) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyUp) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + if(model->position == 0) { + if(model->first_file_index == 0) { + // wrap + int16_t max_first_file_index = model->file_count - FILENAME_COUNT; + model->position = MIN(FILENAME_COUNT - 1, model->file_count - 1); + model->first_file_index = + max_first_file_index < 0 ? 0 : max_first_file_index; + } else { + model->first_file_index--; + } + } else if(model->position == 1) { + if(model->first_file_index == 0) { + model->position--; + } else { + model->first_file_index--; + } + } else { + model->position--; + } + }, + true); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyDown) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + uint16_t max_first_file_index = model->file_count > FILENAME_COUNT ? + model->file_count - FILENAME_COUNT : + 0; + + if(model->position >= MIN(FILENAME_COUNT - 1, model->file_count - 1)) { + if(model->first_file_index >= max_first_file_index) { + // wrap + model->position = 0; + model->first_file_index = 0; + } else { + model->first_file_index++; + } + } else if(model->position >= (FILENAME_COUNT - 2)) { + if(model->first_file_index >= max_first_file_index) { + model->position++; + } else { + model->first_file_index++; + } + } else { + model->position++; + } + }, + true); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyOk) { + if(file_select->callback != NULL) { + size_t files = 0; + if(file_select->buffer) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + files = model->file_count; + strlcpy( + file_select->buffer, + furi_string_get_cstr(model->filename[model->position]), + file_select->buffer_size); + }, + false); + }; + if(files > 0) { + file_select->callback(true, file_select->context); + } + } + consumed = true; + } + } + + return consumed; +} + +static bool file_select_init_inner(FileSelect* file_select) { + bool result = false; + if(file_select->path && file_select->extension && file_select->fs_api) { + if(file_select_fill_count(file_select)) { + if(file_select_fill_strings(file_select)) { + file_select->init_completed = true; + result = true; + } + } + } + + return result; +} + +FileSelect* file_select_alloc() { + FileSelect* file_select = malloc(sizeof(FileSelect)); + file_select->view = view_alloc(); + file_select->fs_api = furi_record_open("storage"); + + view_set_context(file_select->view, file_select); + view_allocate_model(file_select->view, ViewModelTypeLockFree, sizeof(FileSelectModel)); + view_set_draw_callback(file_select->view, file_select_draw_callback); + view_set_input_callback(file_select->view, file_select_input_callback); + + with_view_model( + file_select->view, + FileSelectModel * model, + { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + model->filename[i] = furi_string_alloc(); + } + + model->first_file_index = 0; + model->file_count = 0; + }, + false); + + return file_select; +} + +void file_select_free(FileSelect* file_select) { + furi_assert(file_select); + with_view_model( + file_select->view, + FileSelectModel * model, + { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + furi_string_free(model->filename[i]); + } + }, + false); + view_free(file_select->view); + free(file_select); + furi_record_close("storage"); +} + +View* file_select_get_view(FileSelect* file_select) { + furi_assert(file_select); + return file_select->view; +} + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { + file_select->context = context; + file_select->callback = callback; +} + +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) { + furi_assert(file_select); + file_select->path = path; + file_select->extension = extension; +} + +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size) { + file_select->buffer = buffer; + file_select->buffer_size = buffer_size; + + if(file_select->buffer) { + strlcpy(file_select->buffer, "", file_select->buffer_size); + } +} + +bool file_select_init(FileSelect* file_select) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + return false; + } else { + return true; + } +} + +static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { + bool result = false; + + if((file_info->flags & FSF_DIRECTORY)) { + if(strcmp(file_select->extension, "*") == 0) { + result = true; + } else if(strstr(name, file_select->extension) != NULL) { + result = true; + } + } + + return result; +} + +bool file_select_fill_strings(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint8_t string_counter = 0; + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t first_file_index = 0; + + with_view_model( + file_select->view, + FileSelectModel * model, + { first_file_index = model->first_file_index; }, + false); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(file_counter >= first_file_index) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + furi_string_set(model->filename[string_counter], name); + + if(strcmp(file_select->extension, "*") != 0) { + furi_string_replace_all_str( + model->filename[string_counter], file_select->extension, ""); + } + }, + true); + string_counter++; + + if(string_counter >= FILENAME_COUNT) { + break; + } + } + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +bool file_select_fill_count(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + with_view_model( + file_select->view, FileSelectModel * model, { model->file_count = file_counter; }, false); + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +void file_select_set_selected_file_internal(FileSelect* file_select, const char* filename) { + furi_assert(file_select); + furi_assert(filename); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + if(strlen(filename) == 0) return; + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t file_position = 0; + bool file_found = false; + + FuriString* filename_str = furi_string_alloc_set_str(filename); + if(strcmp(file_select->extension, "*") != 0) { + furi_string_cat_str(filename_str, file_select->extension); + } + + if(!storage_dir_open(directory, file_select->path)) { + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(strcmp(furi_string_get_cstr(filename_str), name) == 0) { + file_found = true; + break; + } + + file_position++; + } + } else { + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + } + + if(file_found) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + uint16_t max_first_file_index = + model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; + + model->first_file_index = file_position; + + if(model->first_file_index > 0) { + model->first_file_index -= 1; + } + + if(model->first_file_index >= max_first_file_index) { + model->first_file_index = max_first_file_index; + } + + model->position = file_position - model->first_file_index; + }, + true); + } + + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); +} + +void file_select_set_selected_file(FileSelect* file_select, const char* filename) { + file_select_set_selected_file_internal(file_select, filename); + + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } +} diff --git a/applications/plugins/vbmigrate/gui/modules/file_select.h b/applications/plugins/vbmigrate/gui/modules/file_select.h new file mode 100644 index 000000000..ed3d5b60c --- /dev/null +++ b/applications/plugins/vbmigrate/gui/modules/file_select.h @@ -0,0 +1,31 @@ +/** + * @file file_select.h + * GUI: FileSelect view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FileSelect FileSelect; + +typedef void (*FileSelectCallback)(bool result, void* context); + +FileSelect* file_select_alloc(); + +void file_select_free(FileSelect* file_select); +View* file_select_get_view(FileSelect* file_select); + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); +bool file_select_init(FileSelect* file_select); +void file_select_set_selected_file(FileSelect* file_select, const char* filename); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/vbmigrate/icons/DolphinMafia_115x62.png b/applications/plugins/vbmigrate/icons/DolphinMafia_115x62.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/plugins/vbmigrate/icons/DolphinNice_96x59.png b/applications/plugins/vbmigrate/icons/DolphinNice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QA)Sl zdD4@f#M8iCym|7VCs7c=gNnj#9z6JU@F>)meoPNz2Ls9f|6cyT-~an|dGX5V(KAOm zjvFl&tO}E3@q0MIzVO5rW@4P?YIKP-Xd4EYn?t0ILD7XPxPl?-ti8fB9GBQ|sx?|G zEtocOMHt(Nk?S)w$IZ+}KD1Xc1$DgQcp3i3(`P(zP=;SlmE@A2#Z9NM8Q`VO#j3rz zY8!~3y$og|lM%R>LJ+wvFEpbJ-{Uoz9$!m5=$X*f4Bro`Rw{!m2{6z_MX+UA2D%|4 zSci7KJ_S@+RU}!H6itw2GijKb1_lq$+y$s%R;>KM89Qb8CZ)b9N$qx9Y$rt$tVoJs z7?P|?swyxGA?$b*MuHbk4jC*Q+JWO!hj<`ngmtn`Gdv5mpM&d{N_)g!IH(k>nG``^ zQbbvD-8iwHbx14tZy5Vpht-acr3wzodSJ7LG$w~&R=k59#fB^z^J?I*uE3T>>~$A= zv}k2`_D4hxGLuL*QZ`HpN(v?gZCb}d+E%e($Qrg470Wh8L!SNcdRo!)nwHX%a#B%p z*|~I9OY7;JrO#Vx(vXMPq8C!=*?8#NVZH}g?Le%V4KSo6s1ni|jzPIeC<&Xy2dXNj zz{L`@9WTDQ6nCkgw1op_1EYLET+l1C>Fg7Np-(rEjMD;|PN}R0nkLjCM1rR3EG3vi zX~a_KYA3hc1AOxR-^6tGo!e{*7ot=XaSLN&)^x7*$R z_;8nL#iBJ=jXt&Rz8&Mh$jFComtIimxkqQi literal 0 HcmV?d00001 diff --git a/applications/plugins/vbmigrate/icons/Touch_26x26.png b/applications/plugins/vbmigrate/icons/Touch_26x26.png new file mode 100644 index 0000000000000000000000000000000000000000..32a9f629394975433175a60ddc5cceafc09049ea GIT binary patch literal 14967 zcmeHtWmH_vwk-sAC&4v%e?ut)M89q;}* zW4!x*o$j%__g=NuthHv%+BPauSy2iNnGhKY3JOg|T0-^ZpYZpF2>YGlv2IFT?#U;l^ZI7*!kg&!@UpP$ee6W0|HJ+8(Q3>x zZPp&L5U~&1hK|Vhwdv4776HSFZnu4f2cJ+=!^1jk+ijNn%cuC87CpHRKi_HYl6L>| zuC5b5*+ahErVz5stkpGt-<7-DHg%gr;S-;fy`qczL+fmls{Nsp!&Bc0rzMUH++$fA zS1;0oE4pi()5B%4Cg!-pLaGaHr$`%r27hE-LFG`-nEVU#+XE$P$NLebwTI(0UoyYy z{zLtf(Fcbdn7khY*ylPESkzr|{tv#p%6+JCE$BG+c;h#^?JJF>eG4nd?m6kJo?gtm zg|DBlaX^^Iz#FyEntnn( z@GH9H?z(o7V2_Y$q`-X@pzC(1Gx&P*NXz567d}bZc00?frAF+1;;QO&VS0omm$V7ry{3|;N9;t}R`)cg` zQC*d4iAkezUdDK5rU@$SneVa1gXYf7+aKlJPL{D#wx6_|obP_oG+lJ1WF_rC6>dK< zG_4hI?SzHN*y!bPT_I|dom!f2`1?Q%yV`D0sMdrAG6!Z_<9IJTt7ISRGCmGt>2DIc@1&0Gg&>FA+jVZ=3srHQW~L-Z79wGIO&o4ZDJQu@ zIB4!4rR84hW+Rk(Z74SU@UEy?v#xy9{iw5l-WlanUfFklB-df%q;awxA^X9b78n(NlhEu0Flq|(NIT|Dsj;~5gN z%pC8m>}%w5BAAFTqo%MI^qxIL8U4m>XIjhb4M*17#(caFSUcs<=nG?+4#-!u6d9Qc zFj+zQv9h6W>&i(!ms$W_OsU#jEx8^4jCuhZ|MUs;yx^Xkfw$V^E=h=coF|)cqoLB5 zoqKZ#s|2Hse_5JwO^=H9|56`?kJ9ii~4#();ku?Ri5Ouv)vk z%)sUiI$pF;{Jqtp5w1

o1{AeI9Adn^`khPGQ%0G)>&~%Cz?TfE_Qo1u-CUBC-u_ z+ZacEKZ9;Ek1qH$$-3fh@QY&7f<(XHm|VO^8soK!=jeQ8&Mv!3u&}k}SCF3dc7_<8 z3D|H$>do#siJ*)i6c!H8Vev4bzTn>1S}0bD*OHVWy?J5+P7?eK*KNnuEoH)C^Eysw zk8Bdk7l`6~j$M~>-B!Qw(k`t#FUvCgTD&^(j$j={$}JO{AHRng;b?C_FImlpPSnV5z`}V3yJ5(L&H)@%*|v}Sstbf4p+wKR9V0Ggw?Xw zbccAJe`jh$aQRl1WqtJ{&hs-Gxxh#nBI#|NLM$(pQnsQ zb(@FC{su+ftJhBg3_rJ~)+&G2gq@x`x7WPs#kJS-!xg}}Umdgg{`T0sL)2L)Ta@3^ zOTO^eo7vd#UeR%#w7yqRt!)6ClX9dGmRhlTzJ`TLjTVG}vYSs?dwhFV4L6)WHTS7N zm{w%(TzfyTk3VZ0J{2&ZOr}8#_Km8HjbHWI&_k-p@NhE(RJvqg=8MXabXJ+*1?X;g zQ%Czj%_V}F9%C_Y7Q-g#-e$4IZ8=lqj(k{pV=I~U#(R5Oo}hKrV>{$-&}+gAnP)ik zynLoow<<{)wyb5?3b0lLvYL6fn=lW5xgy%*!?M4HMh?i@18%(Cs$}Y1Gp4V`ro-f`+L;OUIp2C_c|JAb{2)uw5LBF5C%ctPe5Rl% zWdsa6VyH|bN2usn+q1WXOsTxHA4q$x)>UYii*%&l7TY0z^!IaLGo)kG>ZYeGf#u4c zZ)KsK#2AM?aFe*jr(hB`4>c9A;4~;sN8D#2n}tijc1IoxOYqpK9d)kI4^ZR6?ESLV zQ1^MD=p4m)xzpf=ZDo6<`SAnWwX^9agGA~A>%{7%xM{q?Wz=i(j3vdAlJ9QSgiMzH0>2l-Qp;GFApVy;F%w zMd0y}gaTrl-J%W$KLuk=bb$K4qoGn}l}d!(fTl@hf^Yfbwu*|4{`bHpSgjl&W@Wy2$%ZzFpT2xDs$a}}LHzCXIe$_fF+*t>cU~BSpDVG-CeY9=Faxp!`X(Ih! z)TZ|{Bt|5`*B4xsPPT7;gzv{#B2Yh6N|M@+A&|`pK6ET7>l1~`vexSAX68Gb!5$ji z`fVtP@k62O8=GPE-#WezbT%7Lf8RC3okB6RlpeeQge3?}5yE zI(Re3AcfEP4jrr9`g7ZcP^27kYg z^gYb%HnO;J$4(CnF+zy$t-K!vKKXI~IvzP@v7m`bjNSL`CUG(erae=*^zr%Gvf$y92J$a_j;aU@eTPs!SGl{ zl9^)XuhtOhDQky^1SU2p4wbW+;VJ}97x-7|?yH(=z2^p@4i zB;@^N+=J_cY3?mB&<*|g!oyLTE^d-LQVgi+*u?Ff;1+L$0G*^lVAjb6! z8slb?rG_aEXNa}4N#}Eh3VGXPsXV+p_d;M)MRo~41A z5U_$15u&r+6lZT1)8h3VE$tPGx=nIFkt4m`8JRr&vS+~5+dvhV6AzE9m1$X+wid@tJ>w8WjpPQuz=Dm;ra4Awwj7rFZs%()C=wB(gz5=k&=8|iAF1|;N z8lXG2EYk*c>*3?lzlUV)K(Epz1lzhu!kchT`+=wJ0$EczB3z89NLWyu0#&cOHJ)Tz zK9oE7U_*&$zuXu-;0#Jp9U+afe{Zkd)DW5OP_zoPnTzl)%tcOi6>FvGE**m#tR0jvJH!i)%`oBUTd7$qskB@N?vpEz20_Fd_^cKCdwUsb zVMasD9rzf^C``TbhHvv(x2W6IJZ9P-m{hpxeYbe5R`4im3Q1Kb6+>IK zQpOq(g@Zu!5%0dpzTV(xb>M8>YD|OWV4AeiYwf!WiD_(t>E7|awED_F(E6D7U4B(d zyAe7LuZCE>HcedKzd37Z?!GBaEf0;XfqUxIMj^r5c5y_(qpaQf$~o%v%Uh#Ji9QGP z9vGCK)!Loy>-Wbb)We!tWc3CrB{lpT9|4Pl)64?4l1+E&sby5>eWWQD@b*=ElhsgX zuiP)MF=6`sHBs1KfLW?z&vx49_%ntRzhB?4Bzy1XLtBmHztG90TB~w-yETLBI6dO6C<@L3ttboE+qt=9VG;Da5~b`tVctSx6xp0 zJ1KU<&z$lZ0G~s46eO*6A_8L)>{%lNPkop4dRY0IXO-j2PQ8)Ls}sYzx~FF=Dyzpn z$!8X-O+gQSCUv@=oe?vrE?7(<&&J%4P=?>mKyC=u+ zc$i52`4Zv4$3r>ZwI`pFbC21E&h~*Vy}P|FCepcs^D_^Wi;ALlI4h;gIG3?5CwosI zlFebGM58rTT6J>h5Wf;n!H>ZtIZxbt`inqMY@d?k6d~8ojI~MJeCNzmoAGWAG1lLe zcH{enje@T6J;Cyveai*BBDKEXr6$Rc?$)oMB)%89E!rGaLht~jloAd_3dH6gdhvz17Q8j4} zx(Eo*$H^E*?Z)(Fudfpm4Ru+y3ZH%Rg(OB2=$hb4UO>`)k*t+ z3=`tJ3OJp)MNQoak|Q7Sh!lSpfc>g8=*CY8ue+*<71mhrt%X2;?>S8jO+|_RaqF~H z_Oa)i(8bqhq?zLFv$Xz&3=;O%&6U-PR~O&Hwmuu_b~tO?`qAu0Gkp0%gj&7Nw$5%X z{&8mYcT}codqEIGqA;B>$u-wgZAx^xBoJIXEiT zCU4Oo&JR{jvfjFnY%sY0en(Y{&pdhJVEnfH#1t=Q`+LQ>q;%p`SaSZWB+b2pY6Phe zy6-h&T6C5Nt>JjBMl|dudEelTc>3BL2H_5WF=g?#87r(YnCJOvj;r$|2}2WDk)E~t z!ksgfJ(1N{cPq&IpJjH1HgK8+UARcb0=a|5fYQIeC= z@!4wHzB^puadWYNBG+uR=Zr zrb_-8!mbp8I2Ki6_wzPOYYOd_#7|IXcH@0^j+TD&Eg6yHmACmf2}*>)h{SsXdc~%? zMC(Oc^bjgL7w$#pwvX_TL#sk0aw^z$%=I>g%CQ36uRbzmoD zDG^U(wb8z+2nW9szfjn&rO`;wrbMNOc>02;b#Wx9l;ax?a@G;~cOON`FdJkx+i6-xnXZmS+NqM4DPh)%lDLFbSu$KZfRJk_lEY-{nIFO1GQtju3th?|P^8kO7ysw?&X;3Q9epZ0q3I+X=acVX*forDblao;@X0YfUh`agD5PjFF+Ntk_9-l=3L!%nW~A z$bNg%Tdhyng<42UJ$PalnYkFw{qAe+oba=Bs7^=HrnBQb!FbqOBoSuxwp$DKyEams8~Y6Tr^=yz zUwihJ5k7AY9hka;HdRIx|L zHmvtPftB}flgxJJat-h|;t#5;_S|@WdTZ=^l=k_nPgK!fFbgP|36c*74v&A1ik;vR z&1tb+%Y1)oK5ZM^aQwL7R^+?(k(SGsQDCX^XNSBsJptXz8>=X8GPY(%-7b~zus+fE z**0h^yRFro#da!`8Ok&Hq51r>>7?=5TZ^oyRj=ChH9lawJSLxY02$Yi_VepoNkt15 zO%S(}uORX>2Mju~?sv@b-X_;`LPY?pL`LQu2G$IIV1Cf&|A1^Ebn(eMeigJ~zOc|>7DLoIoje3j$_oNHPW zzNA}9`BH4gAmLi&ag}Z{8W(`m#MgQw9J{spB3DT0ZlWU8r$A&$at9QAW9J$}Iil+6 zA{|NgnF&=n6z4WI*bI=M5F{KAgR>%*UC!&Jdvk9}>%KM6#+z|GKQ>u4yYI++Q^Bok z7lIg9v56xsOq|@hAUEpH8!lS=GKb0;Yz555gM0$PDdmBUI4(i(&S; zG$){im7vLlXy<$cPK?Z#9yRJS1??w+$6_- zT`D{ba$SGOL7cq#RC||IgbZ6KiFaS)9OP#G=7-(Q1@^<2smJLtA|#@!>0h3nuXYUL z7|Z%QE|^1B@JsV-co~Pqb(+lVea99lM_mn$N zU@0szTWkP^vePeD!AhtwD^v66Jj<5!$BUO;?b?!Cvla9xP^R%=kI0gZhCD&Acg(Q@wxpo zzTut#-4IO40mw5QANgcuKnP%9NulXHio%(}cX9?!w zF`F(*WfzOzqNjm=)ajw2+T@I}(jVv7&a18={-O;MRs<5$VG424&^ssNA!ZunjUH9h zzCXaKEu(w%?Vxz%BOP%CusOGq$y>(Xz0`Z?(8$J%Vy!whb3$^mbZ>R#KI@9Or)cfJv!@X7K)`;P zRp0=_%Q?}@&=7Jj5KmSpd-r5e@ldLf@$8*g`e>N~nz+pAYh`vLGSLi=q(%P*70mb9 zSVzY8bV!P*Lsx$u;c!KRyAgSA+PE7aIrd8XmT(KFvvHpZd-Y081BK#f$1-&hA_j)_ zyOs@fH0)3X-^lV>r`B5Km7dRupN|(UCEGT!lVLA+yzLqGQCdSK-T9HFqFk40KEr3i zw(IZB+f;QkvNOEqvi&giVaxCEt7qRSL3y7KGJ)_+J~q#1^yZKiPdbQ$VKxar0le{1 zEeoP++2cF#_|IfsLs9F>(@(&6D7r+{ued0VCJy}6*8(7=kAc7X_}3b&9)4)$KUmBL z+#Lj9N40iJ!Og&BbY|)b-I?1smCwSJb|yBri2%Ro5Y9{vr=+?#>GcR5q-a*CKi@Iq zW+oS*me^>A5fYuvUMLO{`KYsp>6-C}xX8_K5@JMq9 zUB+&3EENLs5mO-FZZdqJ!Ib$HZ(d*wX#FZT4|7R^UvG<{=NWPp^<(*zfYS&AF!d`C zdOz`j!LhXUl#~CJ0s~$mbpC`<9Brv%ni+E)gmJIgJy6?1ZN1R^o|xpPEGxRpM?G|6 zhJYN57JVWz2Mg=g_X?d3i%%PSME9i`ZzsFks$fJ)yJr_hDuMUSZG2e`uEb=vB(h}+ z4Q@hG4ra+_(_mgkjPFsSIGdv3Ikwu%?G_u8bFDhmkb+Gk8*RA zAoe@^cvHZ%Ezl>Z$_yy2Aoam?!MI6Tn}a|`P`@Tvo-3&)A^6^V=e;ANQh2y3w-OxL zZk6cloJ45PjrEt_U9;U)ABl!=g8B=F3!E?6Yw#F~7O{NN!AFD^weVANUs9fzHCgNn z8jJg3w5=oGJ3qDK8tK`88_|O;5$4h&wP%|gKN{29KJ3i-`90&uTE``S@q!@c^Rn`@ zRZ$ua8eu}k4n*setN&?Ms|$v};=r%$^r+||5XI~eG{5Pjk7 zvt`LBpe}H`%2!8Zo*W)Kc8V1DY!T4+=o(}~uOYRZUIFKE19?Z1Xe3D0RyEt@1((~o zxRpqHH&KA6aiN#HBkqTd;ta157oEx$+uhMqMDd{H$*D4ltcam?$6Kp*GxkiO<) zhI(Q?x<$ZIO%FqKQO!$zCBaW5!9ed9OIY0VY}-b)>MtKx685sBz^RQ4pr(R%H<3zz zy_0WpaJ!%0+b%`z*HR8xD`CljzS%ic$EJYw^R5&+%pDe0JgG7y zRG5FSegUgPKXoj5!_%XBF0VK?wiYKJB`P}Zx z6*u^xMXs^vJpX;nrkwLp8QYvtZI=zu&Im@gj&7fy-qZsj7h@{|%r&_(EXSrbx+MCs zYT2`9+&ZMEVNcm+blq>=k)u)1!)%(_O&T#i3SYKy9zt4NQv<5oHUC5-Goua0BD_av&o0DAQg8CnzVnK0DQbwH;>keZvIQJDHwmG`jYrgqMEn`$X90 z%h1=)LbsUT5`H-g^+e0^)y%rC7^>GYBH)uU{75myPPuF}9pEc_m{s@Zu`bm@$tRTY z@n{*Y6O?KD+(YlD<<5KiJQ2Ff5=AF*8St>qsc-r)GtK=r}Jw70XA>zV)R z=3g7Xv6RGXSs9-j4?_7CHVAzEf!leKJk&e1cY=B#Cx2cI*UX;~a?2@n^b|?yaNk+d zXdJdTdOki^AQE-!T6k`QY_gfYWv$Lu7*9ORL>_r{azZb8WzMLnHR0JHL=4$xArhZn zHc&5H>1Wgl(>8I(M0T+4QD_UItm5+DPD&vh!O#)55%5tG60URSjp*L^BGqDhX;*?( zFO=_2_Ab_J&yjRjBq)PdQU?)dTjUdIb5}d3RXEHf{-#pIxNaj^H+oXi0KFh)m4yP+8vavIu?vbfw+GnI&f>m)u zmfAVsYok6#M?0Neh3l-Ej>F(%lBX~+!6B}%i52mSinh%Vhq&66Q#GOk@6qlT^X=AN zLC;Ozvj4-0UtBto&hpsuLR?%r=3qyJubcCTeR%JAyN!57M0=oBJ9g0R~0VYeihaUwF*v) zcK^&J$;BHsyq8{{c$fIx7~lVBLwzSshSb_T!&YECZsIICg!@gq9Q(-0Pq9NqsUU8o z+0D>JvD61}7(HlsLOM|({w87zwv$hbvb#0s1Xx5(HMuPmQdXOhZzWBfhoAw6FroLf zq0XtG85#3*M*W4cU9RsQ_Hz;Yg9>xoYQu&g>j%YWIrD3~A08}{ACqXY+R`qLBe;sb z#AeI;ja%T*!tnKSobjj|zT7Jq0M({j&c$HMl!ABBCQ0$e;D362z~q$jI0m7nl-=7A zPJ8P65x&*E7pUAc)IYY2miFY+!-ZC$(eb(P!yq;(6yu7S~&$WU0l-jp=2DhB4)iEAtz6$&W+M%=3l% zy=S(_8yMF0!Oe(uQLaiB%VK*Dg5gHc9J6+d?^FE2DK^_;w#cwmR(i#CW!F`xUxjLa z3PV!5)bQ|p_Jp$ris6;mqOCjPKA=?jb?qUxOpDLkll(9$n!`6_K1(d%Izv54yM>@C zzzTM9i_?;xub{4E8((ahb6h$beGJ{<(}}@5c{*DdbGz=*B1$=8?Gz%ePFCXIvm+aH zCt9(X0Qvak_JJce_}jg(xmgz-17sBFdnIqO5IhceVPozEMAkDuOpe2hs+<%H(C1aD zAr*?qk_!}Jmy&409q9B4rDFC6Q@HZ$)zbC29v-D`O(0kZuC3(yO;V_0fqNFq`08Te zf|!Vw%(X}O7&*@OD8zzDgJ-Hbhwm;s4cssnoLMu+o{xEheR-B9@)wRIIB$-n>wV=5 zC^^(B%64l64K9|Tvv~!jm;kP$Wn+e8IH6;Mlfrdmpc|w72rKv26zg>p?@N>7D`UFE zR(&#Cf-cSg_fvt`Qr_eFR_%)k zd$GlVB0qX@)}%{}oVj9P7@mp=lwGw5OEo335v*fH?xovl_Y?JVWTuXL=JIr(CyMzf z3YdL|izI7dZhIKy-K*~AfO*F*q(n@P@2tSY%{D^BaK`DVtO6Wi~GP$_D6PTQnnUk3nAn9T0#!e}O zOfKMLYR0E3@$PRBFGqru7A`Ihd@L;P?(WR)9L)Ak<}7Tyyu2)|>@4i;fEN#dv!|U4 z$OB;KO!XV$FANE=vx$?XgNvoT9rh1iy$TCOFQ}B^RsnOQ1}PDo%7#Vc;SP^ z1LVNM#>~oMYs>Pl5za1>ZZ9BzOXz=$a8`d=gUg}{cD8qQG674vf$dzV{uRR1ZU&n*wu7TZcbUFJ%9lr;DZ8Kgs&H+a> z#S7vFaGII`0mdf0TmT+65Ie}!48+3;HvShV89QeekevzmH`EI_v*inpDJK}jZN>@$ zm~xo`0i0YQV*rSiiwnR71Q{CxK|pp^;J-pBIa$7_5@hqQS^b7GeSzZUHs&RX4ZdNlx;vRW-kFRbHLKh)ZX3spH1qPwqP|E&~HB3xVU&(f$Ti2yu2J-KrWtt zI%$HPoL{u~8a+i1DpQo=q2v2 zCKC&gojLerhW|}cf45uy55vXD$q8gP19Jm-Kp-{%Cl3cN0K^401F)KbOgY#=CY|J~o+0{ojP)|dIs#s*+#Q)gr4<7DMy zf2r{w56H*P z{r{sp6K)PpFqoAUz|O&G{4#pnAOH`CF&lu3n}ZF+!^6SN&i=nKdR%;*ynioGfaUiY z`R`g4VEMn){FlSOoWfs5{V&_grQzjD!}5i+U5N+!p2^zs-BZ!9Mz z0rmX*`>CTS>BWNNAg$y4^6cCHdxK74L??PNBDlyXNFwZ_;9)Vd>21MIy_j}nBt+Gv zhdMO%(7VYIaW)^TMmy(|Dc}suj8o}DgRVp=DFF)+7Qa|T^s4v_$MQZsCU-3UaJSq` zK@Y^_#TYV#BVc>qQWRtKYrrY)!u=DSG&N$#B=7hu7r!Gctix!kK`O@hex5iV2T4&0 z&Z`*2_odk7a$s3%iw*!`-Q)C;>tH9!(-B-$y^6o1t$$S4FZgcOp)t=$&sf6hWNJ pbZIQYS6vrI8*}w?f|C%o;o?uW2qWO$0bUA*l95!Ds1!2_`af7o8=n9G literal 0 HcmV?d00001 diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene.c new file mode 100644 index 000000000..fcc168e28 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vb_migrate_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const vb_migrate_on_enter_handlers[])(void*) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const vb_migrate_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const vb_migrate_on_exit_handlers[])(void* context) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers vb_migrate_scene_handlers = { + .on_enter_handlers = vb_migrate_on_enter_handlers, + .on_event_handlers = vb_migrate_on_event_handlers, + .on_exit_handlers = vb_migrate_on_exit_handlers, + .scene_num = VbMigrateSceneNum, +}; diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene.h b/applications/plugins/vbmigrate/scenes/vb_migrate_scene.h new file mode 100644 index 000000000..7187bdf0b --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) VbMigrateScene##id, +typedef enum { +#include "vb_migrate_scene_config.h" + VbMigrateSceneNum, +} VbMigrateScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers vb_migrate_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "vb_migrate_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 "vb_migrate_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 "vb_migrate_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_about.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_about.c new file mode 100644 index 000000000..19dc2f0f0 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_about.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +void vb_migrate_scene_about_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + FuriString* temp_str = furi_string_alloc_printf( + "\e#Information\n" + "Version: %s\n" + "Developed by: cyanic\n" + "GitHub: https://github.com/GMMan/flipperzero-vb-migrate\n" + "\n" + "\e#Description\n" + "Makes transferring\n" + "characters with VB Lab less\n" + "cumbersome", + VB_MIGRATE_VERSION); + + Widget* widget = inst->widget; + widget_add_text_box_element( + widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! VB Migration Assistant \e!\n", + false); + widget_add_text_scroll_element(widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void vb_migrate_scene_about_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_config.h b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_config.h new file mode 100644 index 000000000..f090de9df --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_config.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +ADD_SCENE(vb_migrate, main_menu, MainMenu) +ADD_SCENE(vb_migrate, register, Register) +ADD_SCENE(vb_migrate, register_save, RegisterSave) +ADD_SCENE(vb_migrate, save_success, SaveSuccess) +ADD_SCENE(vb_migrate, select, Select) +ADD_SCENE(vb_migrate, load, Load) +ADD_SCENE(vb_migrate, info, Info) +ADD_SCENE(vb_migrate, dev_menu, DevMenu) +ADD_SCENE(vb_migrate, delete, Delete) +ADD_SCENE(vb_migrate, delete_success, DeleteSuccess) +ADD_SCENE(vb_migrate, from_app, FromApp) +ADD_SCENE(vb_migrate, to_app, ToApp) +ADD_SCENE(vb_migrate, delete_captures, DeleteCaptures) +ADD_SCENE(vb_migrate, delete_captures_success, DeleteCapturesSuccess) +ADD_SCENE(vb_migrate, about, About) diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete.c new file mode 100644 index 000000000..314d0f3c2 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +static void + vb_migrate_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } +} + +void vb_migrate_scene_delete_on_enter(void* context) { + VbMigrate* inst = context; + FuriString* temp_str; + + // Perform your setup here + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + const VbTagProduct* product = vb_tag_find_product(bant); + if(product) + temp_str = furi_string_alloc_printf("\e#Delete %s?\e#", product->short_name); + else + temp_str = furi_string_alloc_set_str("\e#Delete VB?\e#"); + + widget_add_text_box_element( + inst->widget, + 0, + 0, + 128, + 23, + AlignCenter, + AlignCenter, + furi_string_get_cstr(temp_str), + false); + widget_add_button_element( + inst->widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_delete_widget_callback, inst); + widget_add_button_element( + inst->widget, GuiButtonTypeRight, "Delete", vb_migrate_scene_delete_widget_callback, inst); + + furi_string_printf(temp_str, "%s\nCharas. captured: %d", inst->text_store, inst->num_captured); + widget_add_string_multiline_element( + inst->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(vb_migrate_delete(inst, inst->text_store, true)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + inst->scene_manager, VbMigrateSceneSelect); + } + } + } + return consumed; +} + +void vb_migrate_scene_delete_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures.c new file mode 100644 index 000000000..c1c10b68c --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_delete_captures_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } +} + +void vb_migrate_scene_delete_captures_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + widget_add_text_box_element( + inst->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, "\e#Clear captures?\e#", false); + widget_add_button_element( + inst->widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_delete_captures_widget_callback, + inst); + widget_add_button_element( + inst->widget, + GuiButtonTypeRight, + "Delete", + vb_migrate_scene_delete_captures_widget_callback, + inst); + + FuriString* temp_str = + furi_string_alloc_printf("%s\nCharas. captured: %d", inst->text_store, inst->num_captured); + widget_add_string_multiline_element( + inst->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_captures_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(vb_migrate_delete(inst, inst->text_store, false)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteCapturesSuccess); + consumed = true; + } else { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + inst->num_captured = vb_migrate_count_captured_mons(inst, inst->text_store); + } + } + return consumed; +} + +void vb_migrate_scene_delete_captures_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures_success.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures_success.c new file mode 100644 index 000000000..8ba811958 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_captures_success.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "vb_migrate_icons.h" + +static void vb_migrate_scene_delete_captures_success_popup_callback(void* context) { + VbMigrate* inst = context; + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); +} + +void vb_migrate_scene_delete_captures_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Popup* popup = inst->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Cleared", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, inst); + popup_set_callback(popup, vb_migrate_scene_delete_captures_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewPopup); +} + +bool vb_migrate_scene_delete_captures_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = scene_manager_search_and_switch_to_previous_scene( + inst->scene_manager, VbMigrateSceneDevMenu); + } + return consumed; +} + +void vb_migrate_scene_delete_captures_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + popup_reset(inst->popup); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_success.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_success.c new file mode 100644 index 000000000..78386872f --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_delete_success.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "vb_migrate_icons.h" + +static void vb_migrate_scene_delete_success_popup_callback(void* context) { + VbMigrate* inst = context; + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); +} + +void vb_migrate_scene_delete_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Popup* popup = inst->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, inst); + popup_set_callback(popup, vb_migrate_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewPopup); +} + +bool vb_migrate_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + uint32_t back_scenes[] = {VbMigrateSceneSelect, VbMigrateSceneMainMenu}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + inst->scene_manager, back_scenes, COUNT_OF(back_scenes)); + } + return consumed; +} + +void vb_migrate_scene_delete_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + popup_reset(inst->popup); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_dev_menu.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_dev_menu.c new file mode 100644 index 000000000..b756ac27d --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_dev_menu.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +typedef enum { + SubmenuDevMenuIndexTransferFromApp, + SubmenuDevMenuIndexTransferToApp, + SubmenuDevMenuIndexSpoof, + SubmenuDevMenuIndexClearCaptures, + SubmenuDevMenuIndexDeleteVb, + SubmenuDevMenuIndexSpoofSelection, // Always keep this last because we add tag type to it +} SubmenuDevMenuIndex; + +static void vb_migrate_scene_dev_menu_var_list_enter_callback(void* context, uint32_t index) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, index); +} + +static void vb_migrate_scene_dev_menu_spoof_change_callback(VariableItem* item) { + VbMigrate* inst = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + VbTagType tag_type = index + 1; + variable_item_set_current_value_text(item, vb_tag_get_tag_type_name(tag_type)); + view_dispatcher_send_custom_event( + inst->view_dispatcher, SubmenuDevMenuIndexSpoofSelection + tag_type); +} + +void vb_migrate_scene_dev_menu_on_enter(void* context) { + VbMigrate* inst = context; + VariableItemList* variable_list = inst->variable_list; + VariableItem* item; + + variable_item_list_set_enter_callback( + variable_list, vb_migrate_scene_dev_menu_var_list_enter_callback, inst); + variable_item_list_add(variable_list, "Transfer App > Flipper", 0, NULL, NULL); + variable_item_list_add(variable_list, "Transfer Flipper > App", 0, NULL, NULL); + item = variable_item_list_add( + variable_list, + "Spoof Version", + VbTagTypeMax - 1, + vb_migrate_scene_dev_menu_spoof_change_callback, + inst); + variable_item_set_current_value_index(item, inst->override_type - 1); + variable_item_set_current_value_text(item, vb_tag_get_tag_type_name(inst->override_type)); + + variable_item_list_add(variable_list, "Clear Captures", 0, NULL, NULL); + variable_item_list_add(variable_list, "Delete Vital Bracelet", 0, NULL, NULL); + + variable_item_list_set_selected_item( + variable_list, scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneDevMenu)); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewVariableItemList); +} + +bool vb_migrate_scene_dev_menu_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event < SubmenuDevMenuIndexSpoofSelection) { + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneDevMenu, event.event); + } + + if(event.event == SubmenuDevMenuIndexTransferFromApp) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneFromApp); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexTransferToApp) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneToApp); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexClearCaptures) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteCaptures); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexDeleteVb) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDelete); + consumed = true; + } else if(event.event >= SubmenuDevMenuIndexSpoofSelection) { + // Note: skipping SubmenuDevMenuIndexSpoof because there's nothing to do on enter + inst->override_type = event.event - SubmenuDevMenuIndexSpoofSelection; + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_dev_menu_on_exit(void* context) { + VbMigrate* inst = context; + + variable_item_list_reset(inst->variable_list); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_from_app.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_from_app.c new file mode 100644 index 000000000..ea3919bbd --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_from_app.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" +#include "vb_migrate_icons.h" + +#define TAG "vb_migrate_scene_from_app" + +typedef enum { + FromAppStateInitial, + FromAppStateInstructions, + FromAppStateEmulateReady, + FromAppStateEmulateCheckDim, + FromAppStateEmulateTransferFromApp, + FromAppStateTemplateError, + FromAppStateSaveError, +} FromAppState; + +typedef enum { + FromAppEventTypeWidgetLeft, + FromAppEventTypeWidgetRight, + FromAppEventTypeTemplateLoadError, + FromAppEventTypeTagWrite, + FromAppEventTypeCaptureSaveError, + FromAppEventTypeCaptureSaveSuccess, +} FromAppEventType; + +static void + vb_migrate_scene_from_app_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeWidgetLeft); + else if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeWidgetRight); + } +} + +static bool vb_migrate_scene_from_app_worker_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool result = false; + + if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeTagWrite); + result = true; + } + + return result; +} + +static void vb_migrate_scene_from_app_set_nfc_state(VbMigrate* inst, FromAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + if(state == FromAppStateEmulateReady) { + vb_tag_set_status(bant, VbTagStatusReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } else if(state == FromAppStateEmulateCheckDim) { + vb_tag_set_status(bant, VbTagStatusReady | VbTagStatusDimReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } else if(state == FromAppStateEmulateTransferFromApp) { + vb_tag_set_status(bant, 0); + vb_tag_set_operation(bant, VbTagOperationIdle); + } + + // Override tag type + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + vb_tag_set_item_id_no(bant, vb_tag_get_default_product(inst->override_type)); + } +} + +static bool vb_migrate_scene_from_app_is_state_changed(VbMigrate* inst, FromAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(state == FromAppStateEmulateReady) { + return operation == VbTagOperationCheckDim; + } else if(state == FromAppStateEmulateCheckDim) { + return operation == VbTagOperationCheckDim || operation == VbTagOperationReturnFromApp; + } + + return false; +} + +static void vb_migrate_scene_from_app_set_state(VbMigrate* inst, FromAppState state) { + uint32_t curr_state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneFromApp); + if(state != curr_state) { + Widget* widget = inst->widget; + + if(state == FromAppStateInstructions) { + widget_reset(widget); + widget_add_text_scroll_element( + widget, + 0, + 0, + 128, + 45, + "\e#To transfer characters\n" + "\e#to Flipper:\n" + "1. Wake up character from\n" + "storage\n" + "2. Sync character to Flipper\n" + "as if it was a Vital Bracelet.\n" + "Flipper will beep when it is\n" + "ready for the next step\n" + "3. The character is\n" + "automatically saved when the\n" + "transfer is complete, and will\n" + "be ready for another transfer\n" + "4. Repeat the above until you\n" + "have transferred all the\n" + "characters you want\n" + "\n" + "You can cancel at any time to\n" + "finish transferring."); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == FromAppStateEmulateReady) { + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_load_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 20, + AlignCenter, + AlignCenter, + FontPrimary, + "Ready, waiting\nfor Dim check"); + FuriString* temp_str = + furi_string_alloc_printf("Charas. captured: %d", inst->num_captured); + widget_add_string_element( + widget, + 63, + 43, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(widget, 7, 7, &I_Touch_26x26); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + furi_string_free(temp_str); + + vb_migrate_scene_from_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_from_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeTemplateLoadError); + } + vb_migrate_show_loading_popup(inst, false); + } else if(state == FromAppStateEmulateCheckDim) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + "Waiting for\nsecond Dim\ncheck/transfer"); + widget_add_icon_element(widget, 7, 18, &I_Touch_26x26); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_from_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_from_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else if(state == FromAppStateEmulateTransferFromApp) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + "Transfer captured,\nsaving..."); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_nfc_state(inst, state); + notification_message(inst->notifications, &sequence_success); + + // Restore original tag type if necessary + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + vb_tag_set_item_id_no(bant, inst->orig_product); + } + + // Save the tag + inst->next_id = vb_migrate_get_next_id(inst, inst->text_store, inst->next_id, false); + FuriString* save_path = furi_string_alloc_printf( + VB_MIGRATE_CAPTURE_FORMAT, inst->next_id, NFC_APP_EXTENSION); + if(vb_migrate_save_nfc(inst, inst->text_store, furi_string_get_cstr(save_path))) { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeCaptureSaveSuccess); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeCaptureSaveError); + } + furi_string_free(save_path); + } else if(state == FromAppStateTemplateError) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, 80, 31, AlignCenter, AlignCenter, FontPrimary, "Could not\nload template"); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == FromAppStateSaveError) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, 80, 31, AlignCenter, AlignCenter, FontPrimary, "Could not\nsave capture"); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else { + furi_crash("Unknown new state in vb_migrate_scene_from_app_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneFromApp, state); + } +} + +void vb_migrate_scene_from_app_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + inst->next_id = 0; + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneFromApp, FromAppStateInitial); + vb_migrate_scene_from_app_set_state(inst, FromAppStateInstructions); +} + +bool vb_migrate_scene_from_app_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FromAppEventTypeWidgetLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == FromAppEventTypeWidgetRight) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateReady); + consumed = true; + } else if(event.event == FromAppEventTypeTagWrite) { + uint32_t state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneFromApp); + if(vb_migrate_scene_from_app_is_state_changed(inst, state)) { + if(state == FromAppStateEmulateReady) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateCheckDim); + consumed = true; + } else if(state == FromAppStateEmulateCheckDim) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(operation == VbTagOperationReturnFromApp) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_state( + inst, FromAppStateEmulateTransferFromApp); + consumed = true; + } else if(operation == VbTagOperationCheckDim) { + // Don't need to reset tag, but should make a beep + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_success); + vb_migrate_blink_emulate(inst); + } + } + } + } else if(event.event == FromAppEventTypeTemplateLoadError) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateTemplateError); + consumed = true; + } else if(event.event == FromAppEventTypeCaptureSaveError) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateSaveError); + consumed = true; + } else if(event.event == FromAppEventTypeCaptureSaveSuccess) { + ++inst->num_captured; + ++inst->next_id; + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateReady); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_from_app_on_event"); + } + } + return consumed; +} + +void vb_migrate_scene_from_app_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_reset_red); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_info.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_info.c new file mode 100644 index 000000000..e60f5dc0f --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_info.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +static void + vb_migrate_scene_info_button_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } + } +} + +void vb_migrate_scene_info_on_enter(void* context) { + VbMigrate* inst = context; + FuriString* temp_str = furi_string_alloc(); + + // Build info scroll + // Name + furi_string_cat_printf(temp_str, "\ec%s\n", inst->text_store); + + // Type + if(inst->orig_product == NULL) + furi_string_cat(temp_str, "Unknown product\n"); + else + furi_string_cat_printf(temp_str, "\e#%s\n", inst->orig_product->name); + + // Number of mons loaded + furi_string_cat_printf(temp_str, "Charas. captured: %d", inst->num_captured); + + widget_add_text_scroll_element(inst->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + inst->widget, GuiButtonTypeRight, "More", vb_migrate_scene_info_button_callback, inst); + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_info_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + UNUSED(inst); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + // Reset menu selection index + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneDevMenu, 0); + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDevMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + uint32_t back_scenes[] = {VbMigrateSceneSelect, VbMigrateSceneMainMenu}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + inst->scene_manager, back_scenes, COUNT_OF(back_scenes)); + } + return consumed; +} + +void vb_migrate_scene_info_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_load.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_load.c new file mode 100644 index 000000000..8f316a1ed --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_load.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +#define TAG "vb_migrate_scene_load" + +void vb_migrate_scene_load_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + vb_migrate_show_loading_popup(inst, true); + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); +} + +bool vb_migrate_scene_load_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(vb_migrate_load_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + inst->num_captured = vb_migrate_count_captured_mons(inst, inst->text_store); + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + const VbTagProduct* product = vb_tag_find_product(bant); + inst->orig_product = product; + if(product) { + inst->orig_type = product->type; + } else { + inst->orig_type = VbTagTypeUnknown; + } + inst->override_type = inst->orig_type; + + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneInfo); + consumed = true; + } else { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + vb_migrate_show_loading_popup(inst, false); + } + return consumed; +} + +void vb_migrate_scene_load_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_main_menu.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_main_menu.c new file mode 100644 index 000000000..13dea042d --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_main_menu.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +typedef enum { + SubmenuMainIndexRegister, + SubmenuMainIndexSelect, + SubmenuMainIndexAbout, +} SubmenuMainIndex; + +static void vb_migrate_scene_main_menu_submenu_callback(void* context, uint32_t index) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, index); +} + +void vb_migrate_scene_main_menu_on_enter(void* context) { + VbMigrate* inst = context; + Submenu* submenu = inst->submenu; + + submenu_add_item( + submenu, + "Register Vital Bracelet", + SubmenuMainIndexRegister, + vb_migrate_scene_main_menu_submenu_callback, + inst); + submenu_add_item( + submenu, + "Select Vital Bracelet", + SubmenuMainIndexSelect, + vb_migrate_scene_main_menu_submenu_callback, + inst); + submenu_add_item( + submenu, "About", SubmenuMainIndexAbout, vb_migrate_scene_main_menu_submenu_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewMenu); +} + +bool vb_migrate_scene_main_menu_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuMainIndexRegister) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneRegister); + consumed = true; + } else if(event.event == SubmenuMainIndexSelect) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneSelect); + consumed = true; + } else if(event.event == SubmenuMainIndexAbout) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneAbout); + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_main_menu_on_exit(void* context) { + VbMigrate* inst = context; + + submenu_reset(inst->submenu); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register.c new file mode 100644 index 000000000..9d800a0b9 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_tag.h" +#include "../vb_migrate_i.h" +#include "vb_migrate_icons.h" + +typedef enum { + RegisterStateInitial, + RegisterStateInstructionInitial, + RegisterStateInstructionConnect, + RegisterStateCaptureInitial, + RegisterStateCaptureInvalidTag, + RegisterStateCapturePwd, + RegisterStateCaptureFull, + RegisterStateCaptureFailed, + RegisterStateCaptureIncorrectTag, +} RegisterState; + +typedef enum { + RegisterEventTypeNextButton, + RegisterEventTypePrevButton, + RegisterEventTypeVbReadInitial, + RegisterEventTypeVbPwdAuth, + RegisterEventTypeVbReadFullSuccess, + RegisterEventTypeVbReadFullFail, +} RegisterEventType; + +static void + vb_migrate_scene_register_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeNextButton); + else if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypePrevButton); + } +} + +static bool + vb_migrate_scene_register_worker_read_initial_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventReadMfUltralight) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadInitial); + consumed = true; + } + + return consumed; +} + +static bool vb_migrate_scene_register_worker_auth_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventMfUltralightPwdAuth) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbPwdAuth); + consumed = true; + } + + return consumed; +} + +static bool + vb_migrate_scene_register_worker_full_capture_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventMfUltralightPassKey) { + memcpy( + inst->nfc_dev->dev_data.mf_ul_data.auth_key, + inst->captured_pwd, + sizeof(inst->captured_pwd)); + consumed = true; + } else if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event( + inst->view_dispatcher, RegisterEventTypeVbReadFullSuccess); + consumed = true; + } else if(event == NfcWorkerEventFail) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadFullFail); + consumed = true; + } + + return consumed; +} + +static void vb_migrate_scene_register_cleanup_state(VbMigrate* inst, RegisterState state) { + if(state == RegisterStateCaptureInvalidTag || state == RegisterStateCaptureFailed || + state == RegisterStateCaptureIncorrectTag) { + notification_message(inst->notifications, &sequence_reset_red); + } else if( + state == RegisterStateCaptureInitial || state == RegisterStateCapturePwd || + state == RegisterStateCaptureFull) { + vb_migrate_blink_stop(inst); + nfc_worker_stop(inst->worker); + } +} + +static void vb_migrate_scene_register_set_state(VbMigrate* inst, RegisterState state) { + uint32_t curr_state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + if(state != curr_state) { + vb_migrate_scene_register_cleanup_state(inst, curr_state); + Widget* widget = inst->widget; + + if(state == RegisterStateInstructionInitial) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 0, + 0, + AlignLeft, + AlignTop, + FontSecondary, + "Please make sure your current\ncharacter has been sent to\nVB Lab before continuing."); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_register_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == RegisterStateInstructionConnect) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 0, + 0, + AlignLeft, + AlignTop, + FontSecondary, + "Prepare VB Lab:\n" + "1. Ensure current character in\n" + "VB Lab has been put in storage\n" + "2. Open the \"Scan\" screen\n" + "3. Tap \"Vital Bracelet -> App\""); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_register_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == RegisterStateCaptureInitial) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + "Tap Flipper to\nyour Vital\nBracelet"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + nfc_device_clear(inst->nfc_dev); + inst->nfc_dev->dev_data.read_mode = NfcReadModeMfUltralight; + nfc_worker_start( + inst->worker, + NfcWorkerStateRead, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_read_initial_callback, + inst); + vb_migrate_blink_read(inst); + } else if(state == RegisterStateCaptureInvalidTag) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontSecondary, + "Tag is not a valid\nVital Bracelet,\nplease try again"); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == RegisterStateCapturePwd) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + "Tap \"Send\" on\nVB Lab, then tap\nto Flipper"); + widget_add_icon_element(widget, 7, 18, &I_Touch_26x26); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + vb_tag_set_operation(bant, VbTagOperationReady); + vb_tag_set_status(bant, VbTagStatusReady); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_auth_callback, + inst); + vb_migrate_blink_emulate(inst); + } else if(state == RegisterStateCaptureFull) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + "Tap Flipper to\nVital Bracelet\nagain"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + inst->nfc_dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto; + nfc_worker_start( + inst->worker, + NfcWorkerStateReadMfUltralightReadAuth, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_full_capture_callback, + inst); + vb_migrate_blink_read(inst); + } else if(state == RegisterStateCaptureFailed) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontSecondary, + "Failed to read\nVital Bracelet,\nplease try again"); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == RegisterStateCaptureIncorrectTag) { + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontSecondary, + "Different tag read,\nplease try again"); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else { + furi_crash("Unknown new state in vb_migrate_scene_register_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneRegister, state); + } +} + +static bool vb_migrate_scene_register_next_state(VbMigrate* inst, RegisterState state) { + if(state == RegisterStateInstructionInitial) { + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect); + return true; + } else if(state == RegisterStateInstructionConnect) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial); + return true; + } + + return false; +} + +static bool + vb_migrate_scene_register_prev_state(VbMigrate* inst, RegisterState state, bool is_back) { + UNUSED(is_back); + + if(state == RegisterStateInstructionInitial || state == RegisterStateInstructionConnect) { + return scene_manager_previous_scene(inst->scene_manager); + } else if(state == RegisterStateCaptureInitial) { + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect); + return true; + } else if( + state == RegisterStateCaptureInvalidTag || state == RegisterStateCapturePwd || + state == RegisterStateCaptureFull) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial); + return true; + } else if(state == RegisterStateCaptureFailed || state == RegisterStateCaptureIncorrectTag) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull); + return true; + } + + return is_back; +} + +void vb_migrate_scene_register_on_enter(void* context) { + VbMigrate* inst = context; + + scene_manager_set_scene_state( + inst->scene_manager, VbMigrateSceneRegister, RegisterStateInitial); + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionInitial); +} + +bool vb_migrate_scene_register_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + RegisterState state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RegisterEventTypeNextButton) { + consumed = vb_migrate_scene_register_next_state(inst, state); + } else if(event.event == RegisterEventTypePrevButton) { + consumed = vb_migrate_scene_register_prev_state(inst, state, false); + } else if(event.event == RegisterEventTypeVbReadInitial) { + if(vb_tag_validate_product(&inst->nfc_dev->dev_data)) { + memcpy( + inst->captured_uid, + inst->nfc_dev->dev_data.nfc_data.uid, + sizeof(inst->captured_uid)); + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_register_set_state(inst, RegisterStateCapturePwd); + } else { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInvalidTag); + } + consumed = true; + } else if(event.event == RegisterEventTypeVbPwdAuth) { + // Set up for auth + memcpy( + inst->captured_pwd, + inst->nfc_dev->dev_data.mf_ul_auth.pwd.raw, + sizeof(inst->captured_pwd)); + + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull); + consumed = true; + } else if(event.event == RegisterEventTypeVbReadFullSuccess) { + NfcDeviceData* dev_data = &inst->nfc_dev->dev_data; + if(memcmp(dev_data->nfc_data.uid, inst->captured_uid, sizeof(inst->captured_uid)) || + dev_data->mf_ul_data.data_read != dev_data->mf_ul_data.data_size) { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureIncorrectTag); + } else { + notification_message(inst->notifications, &sequence_success); + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneRegisterSave); + } + consumed = true; + } else if(event.event == RegisterEventTypeVbReadFullFail) { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFailed); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_register_on_event"); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = vb_migrate_scene_register_prev_state(inst, state, true); + } + return consumed; +} + +void vb_migrate_scene_register_on_exit(void* context) { + VbMigrate* inst = context; + RegisterState state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + + vb_migrate_scene_register_cleanup_state(inst, state); + widget_reset(inst->widget); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register_save.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register_save.c new file mode 100644 index 000000000..e388ed25e --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_register_save.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +// This thing doesn't know what a FuriString is +#include + +typedef enum { + RegisterSaveEventTextInput, +} RegisterSaveEvent; + +static void vb_migrate_scene_register_save_text_input_callback(void* context) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterSaveEventTextInput); +} + +void vb_migrate_scene_register_save_on_enter(void* context) { + VbMigrate* inst = context; + + // Setup view + TextInput* text_input = inst->text_input; + text_input_set_header_text(text_input, "Name this Vital Bracelet"); + text_input_set_result_callback( + text_input, + vb_migrate_scene_register_save_text_input_callback, + inst, + inst->text_store, + VB_MIGRATE_MAX_DEV_NAME_LENGTH, + true); + + // Set default name + FuriString* temp_str; + NfcDeviceData* dev_data = &inst->nfc_dev->dev_data; + BantBlock* bant = vb_tag_get_bant_block(dev_data); + const VbTagProduct* prod = vb_tag_find_product(bant); + temp_str = furi_string_alloc_printf("%s_", prod->short_name); + for(size_t i = 0; i < dev_data->nfc_data.uid_len; ++i) { + furi_string_cat_printf(temp_str, "%02x", dev_data->nfc_data.uid[i]); + } + vb_migrate_text_store_set(inst, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + // We're validating whether folder exists + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(VB_MIGRATE_FOLDER, "/" VB_MIGRATE_TEMPLATE_NAME, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewTextInput); + notification_message(inst->notifications, &sequence_set_green_255); +} + +bool vb_migrate_scene_register_save_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RegisterSaveEventTextInput) { + if(strlen(inst->text_store) != 0) { + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_save_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + inst->num_captured = 0; + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + const VbTagProduct* product = vb_tag_find_product(bant); + inst->orig_product = product; + if(product) { + inst->orig_type = product->type; + } else { + inst->orig_type = VbTagTypeUnknown; + } + inst->override_type = inst->orig_type; + + // Go to success + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneSaveSuccess); + } else { + // Otherwise just stay here + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewTextInput); + } + vb_migrate_show_loading_popup(inst, false); + + consumed = true; + } + } + } + return consumed; +} + +void vb_migrate_scene_register_save_on_exit(void* context) { + VbMigrate* inst = context; + + // Clear view + TextInput* text_input = inst->text_input; + ValidatorIsFile* validator = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free(validator); + + text_input_reset(text_input); + notification_message_block(inst->notifications, &sequence_reset_green); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_save_success.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_save_success.c new file mode 100644 index 000000000..69d3710a3 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_save_success.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "vb_migrate_icons.h" + +typedef enum { + SaveSuccessEventPopup, +} SaveSuccessEvent; + +static void vb_migrate_scene_save_success_popup_callback(void* context) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, SaveSuccessEventPopup); +} + +void vb_migrate_scene_save_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Setup view + Popup* popup = inst->popup; + 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, inst); + popup_set_callback(popup, vb_migrate_scene_save_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewPopup); +} + +bool vb_migrate_scene_save_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SaveSuccessEventPopup) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneInfo); + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_save_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Clear view + popup_reset(inst->popup); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_select.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_select.c new file mode 100644 index 000000000..84f8590d8 --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_select.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_select_file_select_callback(bool result, void* context) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, result); +} + +void vb_migrate_scene_select_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + file_select_set_callback( + inst->file_select, vb_migrate_scene_select_file_select_callback, inst); + file_select_set_filter(inst->file_select, VB_MIGRATE_FOLDER, "*"); + file_select_set_result_buffer(inst->file_select, inst->text_store, sizeof(inst->text_store)); + file_select_init(inst->file_select); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewFileSelect); +} + +bool vb_migrate_scene_select_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + UNUSED(inst); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event) { + // File selected + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneLoad); + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/vbmigrate/scenes/vb_migrate_scene_to_app.c b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_to_app.c new file mode 100644 index 000000000..5f6055d4b --- /dev/null +++ b/applications/plugins/vbmigrate/scenes/vb_migrate_scene_to_app.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" +#include "vb_migrate_icons.h" + +#define TAG "vb_migrate_scene_to_app" + +typedef enum { + ToAppStateInitial, + ToAppStateInstructions, + ToAppStateEmulateReady, + ToAppStateEmulateTransferToApp, + ToAppStateLoadError, + ToAppStateComplete, +} ToAppState; + +typedef enum { + ToAppEventTypeWidgetLeft, + ToAppEventTypeWidgetRight, + ToAppEventTypeEmulateStart, + ToAppEventTypeCaptureLoadError, + ToAppEventTypeTagWrite, +} ToAppEventType; + +static void + vb_migrate_scene_to_app_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeWidgetLeft); + else if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeWidgetRight); + } +} + +static bool vb_migrate_scene_to_app_worker_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool result = false; + + if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeTagWrite); + result = true; + } + + return result; +} + +static void vb_migrate_scene_to_app_set_nfc_state(VbMigrate* inst, ToAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + if(state == ToAppStateEmulateReady) { + vb_tag_set_status(bant, VbTagStatusReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } + + // Override tag type + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + vb_tag_set_item_id_no(bant, vb_tag_get_default_product(inst->override_type)); + } +} + +static bool vb_migrate_scene_to_app_is_state_changed(VbMigrate* inst, ToAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(state == ToAppStateEmulateReady) { + return operation == VbTagOperationTransferToApp; + } + + return false; +} + +static void vb_migrate_scene_to_app_set_state(VbMigrate* inst, ToAppState state) { + uint32_t curr_state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + if(state != curr_state) { + Widget* widget = inst->widget; + + if(state == ToAppStateInstructions) { + widget_reset(widget); + widget_add_text_scroll_element( + widget, + 0, + 0, + 128, + 45, + "\e#To transfer characters\n" + "\e#from Flipper:\n" + "1. Put the current character\n" + "into storage\n" + "2. Sync character from\n" + "Flipperas if it was a Vital\n" + "Bracelet. Flipper will beep\n" + "and automatically advance to\n" + "the next captured character\n" + "when VB Lab has transferred\n" + "the current character\n" + "\n" + "-> You can press the right\n" + "key to skip sending the\n" + "current character\n" + "3. Repeat the above until you\n" + "have transferred all the\n" + "characters you want\n" + "\n" + "You can cancel at any time to\n" + "finish transferring."); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + widget_add_button_element( + widget, GuiButtonTypeRight, "Next", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == ToAppStateEmulateReady) { + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeEmulateStart); + } else if(state == ToAppStateLoadError) { + FuriString* temp_str = + furi_string_alloc_printf("Could not load\ncapture %03d", inst->next_id); + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 31, + AlignCenter, + AlignCenter, + FontPrimary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(widget, 15, 22, &I_Error_18x18); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + furi_string_free(temp_str); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == ToAppStateComplete) { + widget_reset(widget); + widget_add_icon_element(widget, 32, 5, &I_DolphinNice_96x59); + widget_add_string_multiline_element( + widget, 28, 26, AlignCenter, AlignBottom, FontPrimary, "Transfers\ncomplete"); + widget_add_button_element( + widget, GuiButtonTypeRight, "Done", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else { + furi_crash("Unknown new state in vb_migrate_scene_to_app_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneToApp, state); + } +} + +static void vb_migrate_scene_to_app_load_capture(VbMigrate* inst, bool go_next) { + if(go_next) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + ++inst->next_id; + ++inst->num_sent; + } + + if(inst->num_sent == inst->num_captured) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateComplete); + } else { + uint32_t state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + inst->next_id = vb_migrate_get_next_id(inst, inst->text_store, inst->next_id, true); + FuriString* temp_str = + furi_string_alloc_printf(VB_MIGRATE_CAPTURE_FORMAT, inst->next_id, NFC_APP_EXTENSION); + + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_load_nfc(inst, inst->text_store, furi_string_get_cstr(temp_str))) { + Widget* widget = inst->widget; + + widget_reset(widget); + widget_add_string_multiline_element( + widget, + 80, + 20, + AlignCenter, + AlignCenter, + FontPrimary, + "Ready, waiting\nfor transfer"); + furi_string_printf( + temp_str, "Progress: %d/%d", inst->num_sent + 1, inst->num_captured); + widget_add_string_element( + widget, + 63, + 43, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(widget, 7, 7, &I_Touch_26x26); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + widget_add_button_element( + widget, GuiButtonTypeRight, "Skip", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + vb_migrate_scene_to_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_to_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, ToAppEventTypeCaptureLoadError); + } + vb_migrate_show_loading_popup(inst, false); + furi_string_free(temp_str); + } +} + +void vb_migrate_scene_to_app_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + inst->next_id = 0; + inst->num_sent = 0; + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneToApp, ToAppStateInitial); + vb_migrate_scene_to_app_set_state(inst, ToAppStateInstructions); +} + +bool vb_migrate_scene_to_app_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + uint32_t state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == ToAppEventTypeWidgetLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == ToAppEventTypeWidgetRight) { + if(state == ToAppStateInstructions) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateEmulateReady); + consumed = true; + } else if(state == ToAppStateEmulateReady) { + vb_migrate_scene_to_app_load_capture(inst, true); + consumed = true; + } else if(state == ToAppStateComplete) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + } else if(event.event == ToAppEventTypeEmulateStart) { + vb_migrate_scene_to_app_load_capture(inst, false); + } else if(event.event == ToAppEventTypeTagWrite) { + if(vb_migrate_scene_to_app_is_state_changed(inst, state)) { + if(state == ToAppStateEmulateReady) { + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_to_app_load_capture(inst, true); + consumed = true; + } + } + } else if(event.event == ToAppEventTypeCaptureLoadError) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateLoadError); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_to_app_on_event"); + } + } + return consumed; +} + +void vb_migrate_scene_to_app_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_reset_red); +} diff --git a/applications/plugins/vbmigrate/vb_migrate.c b/applications/plugins/vbmigrate/vb_migrate.c new file mode 100644 index 000000000..fec624a0b --- /dev/null +++ b/applications/plugins/vbmigrate/vb_migrate.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "vb_migrate_i.h" + +#define TAG "vb_migrate" + +bool vb_migrate_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + VbMigrate* inst = context; + return scene_manager_handle_custom_event(inst->scene_manager, event); +} + +bool vb_migrate_back_event_callback(void* context) { + furi_assert(context); + VbMigrate* inst = context; + return scene_manager_handle_back_event(inst->scene_manager); +} + +void vb_migrate_blink_read(VbMigrate* inst) { + notification_message(inst->notifications, &sequence_blink_start_cyan); +} + +void vb_migrate_blink_emulate(VbMigrate* inst) { + notification_message(inst->notifications, &sequence_blink_start_magenta); +} + +void vb_migrate_blink_stop(VbMigrate* inst) { + notification_message_block(inst->notifications, &sequence_blink_stop); +} + +void vb_migrate_text_store_set(VbMigrate* inst, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(inst->text_store, sizeof(inst->text_store), text, args); + + va_end(args); +} + +void vb_migrate_text_store_clear(VbMigrate* inst) { + memset(inst->text_store, 0, sizeof(inst->text_store)); +} + +bool vb_migrate_save_nfc(VbMigrate* inst, const char* dev_name, const char* file_name) { + bool saved = false; + FuriString* temp_str = furi_string_alloc(); + + do { + furi_string_printf(temp_str, "%s/%s", VB_MIGRATE_FOLDER, dev_name); + if(!storage_simply_mkdir(inst->storage, furi_string_get_cstr(temp_str))) { + dialog_message_show_storage_error(inst->dialogs, "Can not create\ndata folder"); + break; + } + furi_string_cat_printf(temp_str, "/%s", file_name); + inst->nfc_dev->format = NfcDeviceSaveFormatMifareUl; + saved = nfc_device_save(inst->nfc_dev, furi_string_get_cstr(temp_str)); + } while(false); + + furi_string_free(temp_str); + return saved; +} + +bool vb_migrate_load_nfc(VbMigrate* inst, const char* dev_name, const char* file_name) { + bool saved = false; + FuriString* temp_str = + furi_string_alloc_printf("%s/%s/%s", VB_MIGRATE_FOLDER, dev_name, file_name); + saved = nfc_device_load(inst->nfc_dev, furi_string_get_cstr(temp_str), true); + + furi_string_free(temp_str); + return saved; +} + +bool vb_migrate_delete(VbMigrate* inst, const char* dev_name, bool whole_vb) { + bool deleted = false; + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + + if(whole_vb) { + deleted = storage_simply_remove_recursive(inst->storage, furi_string_get_cstr(dir_path)); + } else { + File* dir_handle = storage_file_alloc(inst->storage); + if(storage_dir_open(dir_handle, furi_string_get_cstr(dir_path))) { + FileInfo file_info; + char name[256]; + FuriString* file_path = furi_string_alloc(); + while(storage_dir_read(dir_handle, &file_info, name, sizeof(name))) { + // Files that is .nfc, but is not template + if(!(file_info.flags & FSF_DIRECTORY) && strstr(name, NFC_APP_EXTENSION) && + !strstr(name, VB_MIGRATE_TEMPLATE_NAME)) { + furi_string_printf(file_path, "%s/%s", furi_string_get_cstr(dir_path), name); + deleted = + storage_simply_remove(inst->storage, furi_string_get_cstr(file_path)); + if(!deleted) break; + } + } + + furi_string_free(file_path); + storage_dir_close(dir_handle); + } + storage_file_free(dir_handle); + } + + furi_string_free(dir_path); + return deleted; +} + +int vb_migrate_count_captured_mons(VbMigrate* inst, const char* dev_name) { + int count = 0; + + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + File* dir_handle = storage_file_alloc(inst->storage); + if(storage_dir_open(dir_handle, furi_string_get_cstr(dir_path))) { + FileInfo file_info; + char name[256]; + while(storage_dir_read(dir_handle, &file_info, name, sizeof(name))) { + // Files that is .nfc, but is not template + if(!(file_info.flags & FSF_DIRECTORY) && strstr(name, NFC_APP_EXTENSION) && + !strstr(name, VB_MIGRATE_TEMPLATE_NAME)) + ++count; + } + + storage_dir_close(dir_handle); + } + storage_file_free(dir_handle); + furi_string_free(dir_path); + + return count; +} + +int vb_migrate_get_next_id(VbMigrate* inst, const char* dev_name, int i, bool is_load) { + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + FuriString* file_path = furi_string_alloc(); + while(true) { + furi_string_printf( + file_path, + "%s/" VB_MIGRATE_CAPTURE_FORMAT, + furi_string_get_cstr(dir_path), + i, + NFC_APP_EXTENSION); + bool exit_cond = + storage_common_stat(inst->storage, furi_string_get_cstr(file_path), NULL) == + FSE_NOT_EXIST; + if(is_load) exit_cond = !exit_cond; + if(exit_cond) break; + ++i; + } + + furi_string_free(file_path); + furi_string_free(dir_path); + return i; +} + +void vb_migrate_show_loading_popup(VbMigrate* inst, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +VbMigrate* vb_migrate_alloc() { + VbMigrate* inst = malloc(sizeof(VbMigrate)); + + inst->view_dispatcher = view_dispatcher_alloc(); + inst->scene_manager = scene_manager_alloc(&vb_migrate_scene_handlers, inst); + view_dispatcher_enable_queue(inst->view_dispatcher); + view_dispatcher_set_event_callback_context(inst->view_dispatcher, inst); + view_dispatcher_set_custom_event_callback( + inst->view_dispatcher, vb_migrate_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + inst->view_dispatcher, vb_migrate_back_event_callback); + + // GUI + inst->gui = furi_record_open(RECORD_GUI); + + // Storage + inst->storage = furi_record_open(RECORD_STORAGE); + + // Notifications service + inst->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Dialogs + inst->dialogs = furi_record_open(RECORD_DIALOGS); + + // NFC + inst->nfc_dev = nfc_device_alloc(); + inst->worker = nfc_worker_alloc(); + + // Submenu + inst->submenu = submenu_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewMenu, submenu_get_view(inst->submenu)); + + // Popup + inst->popup = popup_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewPopup, popup_get_view(inst->popup)); + + // Widget + inst->widget = widget_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewWidget, widget_get_view(inst->widget)); + + // File select + inst->file_select = file_select_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewFileSelect, file_select_get_view(inst->file_select)); + + // Text input + inst->text_input = text_input_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewTextInput, text_input_get_view(inst->text_input)); + + // Loading + inst->loading = loading_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewLoading, loading_get_view(inst->loading)); + + // Variable item list + inst->variable_list = variable_item_list_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, + VbMigrateViewVariableItemList, + variable_item_list_get_view(inst->variable_list)); + + // Dialog ex + // inst->dialog_ex = dialog_ex_alloc(); + // view_dispatcher_add_view( + // inst->view_dispatcher, VbMigrateViewDialogEx, dialog_ex_get_view(inst->dialog_ex)); + + return inst; +} + +void vb_migrate_free(VbMigrate* inst) { + // Dialog ex + // view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewDialogEx); + // dialog_ex_free(inst->dialog_ex); + + // Variable item list + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewVariableItemList); + variable_item_list_free(inst->variable_list); + + // Loading + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewLoading); + loading_free(inst->loading); + + // Text input + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewTextInput); + text_input_free(inst->text_input); + + // File select + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewFileSelect); + file_select_free(inst->file_select); + + // Widget + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewWidget); + widget_free(inst->widget); + + // Popup + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewPopup); + popup_free(inst->popup); + + // Submenu + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewMenu); + submenu_free(inst->submenu); + + // NFC + nfc_worker_free(inst->worker); + nfc_device_free(inst->nfc_dev); + + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_GUI); + + view_dispatcher_free(inst->view_dispatcher); + scene_manager_free(inst->scene_manager); + + free(inst); +} + +int32_t vb_migrate_app(void* p) { + UNUSED(p); + + VbMigrate* inst = vb_migrate_alloc(); + view_dispatcher_attach_to_gui(inst->view_dispatcher, inst->gui, ViewDispatcherTypeFullscreen); + + if(storage_simply_mkdir(inst->storage, VB_MIGRATE_FOLDER)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneMainMenu); + view_dispatcher_run(inst->view_dispatcher); + } else { + dialog_message_show_storage_error(inst->dialogs, "Can not create\napp folder"); + } + + vb_migrate_free(inst); + return 0; +} diff --git a/applications/plugins/vbmigrate/vb_migrate.h b/applications/plugins/vbmigrate/vb_migrate.h new file mode 100644 index 000000000..886ba0773 --- /dev/null +++ b/applications/plugins/vbmigrate/vb_migrate.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#define VB_MIGRATE_VERSION "0.2" + +#define VB_MIGRATE_FOLDER ANY_PATH("vb_migrate") + +typedef struct VbMigrate VbMigrate; + +VbMigrate* vb_migrate_alloc(); + +void vb_migrate_free(VbMigrate* inst); diff --git a/applications/plugins/vbmigrate/vb_migrate_i.h b/applications/plugins/vbmigrate/vb_migrate_i.h new file mode 100644 index 000000000..51a32318e --- /dev/null +++ b/applications/plugins/vbmigrate/vb_migrate_i.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include "gui/modules/file_select.h" +#include +#include + +#include +#include + +#include + +#include "vb_migrate.h" +#include "scenes/vb_migrate_scene.h" +#include "vb_tag.h" + +#define VB_MIGRATE_TEMPLATE_NAME "template" NFC_APP_EXTENSION +#define VB_MIGRATE_CAPTURE_FORMAT "%03d%s" + +#define VB_MIGRATE_MAX_DEV_NAME_LENGTH (30) + +struct VbMigrate { + Gui* gui; + Storage* storage; + DialogsApp* dialogs; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Submenu* submenu; + Popup* popup; + Widget* widget; + // DialogEx* dialog_ex; + FileSelect* file_select; + TextInput* text_input; + Loading* loading; + VariableItemList* variable_list; + NfcWorker* worker; + NfcDevice* nfc_dev; + char text_store[128]; + uint8_t captured_pwd[4]; + uint8_t captured_uid[7]; + int num_captured; + int next_id; + int num_sent; + VbTagType orig_type; + VbTagType override_type; + const VbTagProduct* orig_product; +}; + +typedef enum { + VbMigrateViewMenu, + VbMigrateViewPopup, + VbMigrateViewWidget, + VbMigrateViewTextInput, + // VbMigrateViewDialogEx, + VbMigrateViewFileSelect, + VbMigrateViewLoading, + VbMigrateViewVariableItemList, +} VbMigrateView; + +void vb_migrate_blink_read(VbMigrate* inst); +void vb_migrate_blink_emulate(VbMigrate* inst); +void vb_migrate_blink_stop(VbMigrate* inst); +void vb_migrate_text_store_set(VbMigrate* inst, const char* text, ...); +void vb_migrate_text_store_clear(VbMigrate* inst); +bool vb_migrate_save_nfc(VbMigrate* inst, const char* dev_name, const char* file_name); +bool vb_migrate_load_nfc(VbMigrate* inst, const char* dev_name, const char* file_name); +int vb_migrate_count_captured_mons(VbMigrate* inst, const char* dev_name); +bool vb_migrate_delete(VbMigrate* inst, const char* dev_name, bool whole_vb); +int vb_migrate_get_next_id(VbMigrate* inst, const char* dev_name, int i, bool is_load); +void vb_migrate_show_loading_popup(VbMigrate* inst, bool show); diff --git a/applications/plugins/vbmigrate/vb_tag.c b/applications/plugins/vbmigrate/vb_tag.c new file mode 100644 index 000000000..ce8edf7d5 --- /dev/null +++ b/applications/plugins/vbmigrate/vb_tag.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vb_tag.h" + +#define VB_NAME_VBDM "VB Digital Monster" +#define VB_NAME_VBV "VB Digivice -V-" +#define VB_NAME_VBC "VB Characters" +#define VB_NAME_VH "Vital Hero" + +#define VB_NAME_VBDM_SHORT "VBDM" +#define VB_NAME_VBV_SHORT "VBV" +#define VB_NAME_VBC_SHORT "VBC" +#define VB_NAME_VH_SHORT "VH" + +static const VbTagProduct vb_tag_valid_products[] = { + {.item_id = 0x0200, + .item_no = 0x0100, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0200, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0300, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0400, + .name = VB_NAME_VBV, + .short_name = VB_NAME_VBV_SHORT, + .type = VbTagTypeVBV}, + {.item_id = 0x0200, + .item_no = 0x0500, + .name = VB_NAME_VBV, + .short_name = VB_NAME_VBV_SHORT, + .type = VbTagTypeVBV}, + {.item_id = 0x0200, + .item_no = 0x0600, + .name = VB_NAME_VH, + .short_name = VB_NAME_VH_SHORT, + .type = VbTagTypeVH}, + {.item_id = 0x0300, + .item_no = 0x0100, + .name = VB_NAME_VBC, + .short_name = VB_NAME_VBC_SHORT, + .type = VbTagTypeVBC}, +}; + +static const char* vb_tag_type_names[] = { + "Unknown", + VB_NAME_VBDM_SHORT, + VB_NAME_VBV_SHORT, + VB_NAME_VBC_SHORT, + VB_NAME_VH_SHORT, +}; + +BantBlock* vb_tag_get_bant_block(NfcDeviceData* dev) { + return (BantBlock*)&dev->mf_ul_data.data[16]; +} + +const VbTagProduct* vb_tag_find_product(const BantBlock* bant) { + for(size_t i = 0; i < COUNT_OF(vb_tag_valid_products); ++i) { + const VbTagProduct* product = &vb_tag_valid_products[i]; + if(bant->item_id == product->item_id && bant->item_no == product->item_no) return product; + } + + return NULL; +} + +bool vb_tag_validate_product(NfcDeviceData* dev) { + // Must be NTAG I2C Plus 1k + if(dev->protocol != NfcDeviceProtocolMifareUl) return false; + if(dev->mf_ul_data.type != MfUltralightTypeNTAGI2CPlus1K) return false; + // Must match one of the known product IDs + BantBlock* bant = vb_tag_get_bant_block(dev); + if(bant->magic != BANT_MAGIC) return false; + return vb_tag_find_product(bant) != NULL; +} + +VbTagStatus vb_tag_get_status(const BantBlock* bant) { + return bant->status; +} + +void vb_tag_set_status(BantBlock* bant, VbTagStatus status) { + bant->status = status; +} + +VbTagOperation vb_tag_get_operation(const BantBlock* bant) { + return bant->operation; +} + +void vb_tag_set_operation(BantBlock* bant, VbTagOperation operation) { + bant->operation = operation; +} + +const VbTagProduct* vb_tag_get_default_product(VbTagType type) { + // IMPORTANT: Update when vb_tag_valid_products changes + switch(type) { + case VbTagTypeVBDM: + return &vb_tag_valid_products[2]; + case VbTagTypeVBV: + return &vb_tag_valid_products[4]; + case VbTagTypeVBC: + return &vb_tag_valid_products[6]; + case VbTagTypeVH: + return &vb_tag_valid_products[5]; + + default: + return NULL; + } +} + +void vb_tag_set_item_id_no(BantBlock* bant, const VbTagProduct* product) { + bant->item_id = product->item_id; + bant->item_no = product->item_no; +} + +const char* vb_tag_get_tag_type_name(VbTagType type) { + if(type < VbTagTypeMax) { + return vb_tag_type_names[type]; + } else { + return NULL; + } +} diff --git a/applications/plugins/vbmigrate/vb_tag.h b/applications/plugins/vbmigrate/vb_tag.h new file mode 100644 index 000000000..e101bade2 --- /dev/null +++ b/applications/plugins/vbmigrate/vb_tag.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +#define BANT_MAGIC (0x544E4142) + +typedef struct { + uint32_t magic; + // Note: this should be big endian, but for convenience, we'll treat them as little endian + uint16_t item_id; + uint16_t item_no; + uint8_t status; + uint8_t dim_no; + uint8_t operation; + uint8_t reserved; + uint8_t app_flag; + uint8_t padding[3]; +} __attribute__((packed)) BantBlock; + +typedef enum { + VbTagTypeUnknown, + VbTagTypeVBDM, + VbTagTypeVBV, + VbTagTypeVBC, + VbTagTypeVH, + VbTagTypeMax +} VbTagType; + +typedef struct { + uint16_t item_id; + uint16_t item_no; + const char* name; + const char* short_name; + VbTagType type; +} VbTagProduct; + +typedef enum { + VbTagStatusReady = 1 << 0, + VbTagStatusDimReady = 1 << 1, +} VbTagStatus; + +typedef enum { + VbTagOperationIdle, + VbTagOperationReady, + VbTagOperationTransferToApp, + VbTagOperationCheckDim, + VbTagOperationReturnFromApp, + VbTagOperationSpotInit, + VbTagOperationSpotCommit, +} VbTagOperation; + +BantBlock* vb_tag_get_bant_block(NfcDeviceData* dev); +const VbTagProduct* vb_tag_find_product(const BantBlock* bant); +bool vb_tag_validate_product(NfcDeviceData* dev); +VbTagStatus vb_tag_get_status(const BantBlock* bant); +void vb_tag_set_status(BantBlock* bant, VbTagStatus status); +VbTagOperation vb_tag_get_operation(const BantBlock* bant); +void vb_tag_set_operation(BantBlock* bant, VbTagOperation operation); +const VbTagProduct* vb_tag_get_default_product(VbTagType type); +void vb_tag_set_item_id_no(BantBlock* bant, const VbTagProduct* product); +const char* vb_tag_get_tag_type_name(VbTagType type);