From 52680fd14e025056bedc538d53e2c9e54b4da5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sat, 4 Feb 2023 01:09:20 +0700 Subject: [PATCH 01/75] FreeRTOS: update to 10.5.1 (#2353) --- firmware/targets/f7/api_symbols.csv | 12 +++++++----- lib/FreeRTOS-Kernel | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7b247daed..02f476cc0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.9,, +Version,+,11.10,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2108,6 +2108,7 @@ Function,-,putchar_unlocked,int,int Function,-,putenv,int,char* Function,-,puts,int,const char* Function,-,putw,int,"int, FILE*" +Function,-,pvPortCalloc,void*,"size_t, size_t" Function,-,pvPortMalloc,void*,size_t Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, @@ -2797,11 +2798,11 @@ Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" Function,-,vTaskSetTimeOutState,void,TimeOut_t* Function,-,vTaskStartScheduler,void, -Function,-,vTaskStepTick,void,const TickType_t +Function,-,vTaskStepTick,void,TickType_t Function,-,vTaskSuspend,void,TaskHandle_t Function,-,vTaskSuspendAll,void, Function,-,vTaskSwitchContext,void, -Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const UBaseType_t" +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" @@ -2942,12 +2943,13 @@ Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* Function,-,xTaskResumeAll,BaseType_t, Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t -Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t" -Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" Function,-,xTimerCreateTimerTask,BaseType_t, Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t +Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel index 4c4089b15..def7d2df2 160000 --- a/lib/FreeRTOS-Kernel +++ b/lib/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit 4c4089b1544b590ed3b72491a15365ec020c921f +Subproject commit def7d2df2b0506d3d249334974f51e427c17a41c From a67af49f548c761ac8e207c64ea87b9bd77be5d7 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 3 Feb 2023 22:42:37 +0100 Subject: [PATCH 02/75] Update FreeRTOS-Kernel --- lib/FreeRTOS-Kernel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel index def7d2df2..4c4089b15 160000 --- a/lib/FreeRTOS-Kernel +++ b/lib/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit def7d2df2b0506d3d249334974f51e427c17a41c +Subproject commit 4c4089b1544b590ed3b72491a15365ec020c921f From 1e6e7b8669d725b1cfb2b6053e0d8f5c78d517ca Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 13:56:53 +0000 Subject: [PATCH 03/75] Update symbols --- firmware/targets/f7/api_symbols.csv | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b154c5c76..e71a86827 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.6,, +Version,+,12.7,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2188,7 +2188,6 @@ Function,-,putchar_unlocked,int,int Function,-,putenv,int,char* Function,-,puts,int,const char* Function,-,putw,int,"int, FILE*" -Function,-,pvPortCalloc,void*,"size_t, size_t" Function,-,pvPortMalloc,void*,size_t Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, @@ -4401,11 +4400,11 @@ Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" Function,-,vTaskSetTimeOutState,void,TimeOut_t* Function,-,vTaskStartScheduler,void, -Function,-,vTaskStepTick,void,TickType_t +Function,-,vTaskStepTick,void,const TickType_t Function,-,vTaskSuspend,void,TaskHandle_t Function,-,vTaskSuspendAll,void, Function,-,vTaskSwitchContext,void, -Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const UBaseType_t" Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" @@ -4563,13 +4562,12 @@ Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* Function,-,xTaskResumeAll,BaseType_t, Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t -Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" -Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" Function,-,xTimerCreateTimerTask,BaseType_t, Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t -Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" From 9f279ac87268365e39a1571f60c42be94cd7d3f8 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Mon, 6 Feb 2023 17:03:29 +0300 Subject: [PATCH 04/75] [FL-2744] SPI Mem Manager C port (#1860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .gitignore | 1 + .../plugins/spi_mem_manager/application.fam | 17 + .../images/ChipLooking_64x64/frame_01.png | Bin 0 -> 7231 bytes .../images/ChipLooking_64x64/frame_02.png | Bin 0 -> 6909 bytes .../images/ChipLooking_64x64/frame_03.png | Bin 0 -> 7308 bytes .../images/ChipLooking_64x64/frame_rate | 1 + .../spi_mem_manager/images/Dip8_10px.png | Bin 0 -> 195 bytes .../spi_mem_manager/images/Dip8_32x36.png | Bin 0 -> 977 bytes .../images/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../images/DolphinNice_96x59.png | Bin 0 -> 2459 bytes .../images/SDQuestion_35x43.png | Bin 0 -> 1950 bytes .../images/Wiring_SPI_128x64.png | Bin 0 -> 4446 bytes .../spi_mem_manager/lib/spi/spi_mem_chip.c | 105 ++ .../spi_mem_manager/lib/spi/spi_mem_chip.h | 34 + .../lib/spi/spi_mem_chip_arr.c | 1399 +++++++++++++++++ .../spi_mem_manager/lib/spi/spi_mem_chip_i.h | 85 + .../spi_mem_manager/lib/spi/spi_mem_tools.c | 152 ++ .../spi_mem_manager/lib/spi/spi_mem_tools.h | 14 + .../spi_mem_manager/lib/spi/spi_mem_worker.c | 129 ++ .../spi_mem_manager/lib/spi/spi_mem_worker.h | 54 + .../lib/spi/spi_mem_worker_i.h | 24 + .../lib/spi/spi_mem_worker_modes.c | 214 +++ .../spi_mem_manager/scenes/spi_mem_scene.c | 30 + .../spi_mem_manager/scenes/spi_mem_scene.h | 29 + .../scenes/spi_mem_scene_about.c | 42 + .../scenes/spi_mem_scene_chip_detect.c | 37 + .../scenes/spi_mem_scene_chip_detect_fail.c | 57 + .../scenes/spi_mem_scene_chip_detected.c | 94 ++ .../scenes/spi_mem_scene_chip_error.c | 52 + .../scenes/spi_mem_scene_config.h | 21 + .../scenes/spi_mem_scene_delete_confirm.c | 62 + .../scenes/spi_mem_scene_erase.c | 65 + .../scenes/spi_mem_scene_file_info.c | 29 + .../scenes/spi_mem_scene_read.c | 57 + .../scenes/spi_mem_scene_read_filename.c | 46 + .../scenes/spi_mem_scene_saved_file_menu.c | 76 + .../scenes/spi_mem_scene_select_file.c | 22 + .../scenes/spi_mem_scene_select_model.c | 45 + .../scenes/spi_mem_scene_select_vendor.c | 70 + .../scenes/spi_mem_scene_start.c | 84 + .../scenes/spi_mem_scene_storage_error.c | 56 + .../scenes/spi_mem_scene_success.c | 40 + .../scenes/spi_mem_scene_verify.c | 59 + .../scenes/spi_mem_scene_verify_error.c | 43 + .../scenes/spi_mem_scene_wiring.c | 18 + .../scenes/spi_mem_scene_write.c | 58 + .../plugins/spi_mem_manager/spi_mem_app.c | 112 ++ .../plugins/spi_mem_manager/spi_mem_app.h | 3 + .../plugins/spi_mem_manager/spi_mem_app_i.h | 79 + .../plugins/spi_mem_manager/spi_mem_files.c | 74 + .../plugins/spi_mem_manager/spi_mem_files.h | 14 + .../plugins/spi_mem_manager/tools/README.md | 7 + .../spi_mem_manager/tools/chiplist/LICENSE | 22 + .../tools/chiplist/chiplist.xml | 984 ++++++++++++ .../spi_mem_manager/tools/chiplist_convert.py | 109 ++ .../views/spi_mem_view_detect.c | 64 + .../views/spi_mem_view_detect.h | 9 + .../views/spi_mem_view_progress.c | 230 +++ .../views/spi_mem_view_progress.h | 26 + 59 files changed, 5154 insertions(+) create mode 100644 applications/plugins/spi_mem_manager/application.fam create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate create mode 100644 applications/plugins/spi_mem_manager/images/Dip8_10px.png create mode 100644 applications/plugins/spi_mem_manager/images/Dip8_32x36.png create mode 100644 applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png create mode 100644 applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png create mode 100644 applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png create mode 100644 applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app.h create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app_i.h create mode 100644 applications/plugins/spi_mem_manager/spi_mem_files.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_files.h create mode 100644 applications/plugins/spi_mem_manager/tools/README.md create mode 100644 applications/plugins/spi_mem_manager/tools/chiplist/LICENSE create mode 100644 applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml create mode 100755 applications/plugins/spi_mem_manager/tools/chiplist_convert.py create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h diff --git a/.gitignore b/.gitignore index d54ed4a19..542652eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.swp +*.swo *.gdb_history diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/plugins/spi_mem_manager/application.fam new file mode 100644 index 000000000..09d801876 --- /dev/null +++ b/applications/plugins/spi_mem_manager/application.fam @@ -0,0 +1,17 @@ +App( + appid="spi_mem_manager", + name="SPI Mem Manager", + apptype=FlipperAppType.EXTERNAL, + entry_point="spi_mem_app", + requires=["gui"], + stack_size=1 * 2048, + order=30, + fap_icon="images/Dip8_10px.png", + fap_category="Tools", + fap_icon_assets="images", + fap_private_libs=[ + Lib( + name="spi", + ), + ], +) diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff2e3042e540d8b499118e9edf4ecd180b39511 GIT binary patch literal 7231 zcmeHMdoEn$MdVgUZYLx{p`@Z* z5=qFBa!ZuRNt8&rOMYMKoYPtFyMFIFYrXG(XJ)?NneTq~{ycks_I{rA>^1SW)@IxJ zr1;p_*tT1k8{325`WqJy7kDov2Mx2a@x2OjbY2M`Byg}q zdditUP#58HOC|7r1H~oe0_oV1h2XiPZ*3RSD?Ykq-%_5SOb`1{+$2vyuL;!B7!E_u zzX_}%{q0OK^*Y|$y`*W)GjXA=-S#66!AfzrlUA>&^-udbIVtuzrTN=&La*17lgs*J zZl^wCQ!w$|rq%qI+E;bj$Z_Y0j7d02JuK6t{ia>Ue&qw{8KqC1_FEM{Z2$HNcQIrt zmtb*KajG`z&@Vc&_6PXJ-|^QrK}&6l-0eOsM21&-Za+XW>13C-I#{e~QvIH$q7bVw zK?;(!s|e5Csc+&sXea?3IoYB_NRKx5@~;pPkvx*W^;wl$a4hHfs#-(yds1UZWQ@7{ zA4ybPE~^ex+v3z|$>Z#+O^Hs4sqF82aQv&(h*7&#ox6YTyv6`24Ruzb3o|2;m!=sx z->)?j0g;Q~+f&}eB_7TrA1e~9c7iG`T%!6=!r?&yFKTa+^+bw&|BK($ho)|J$(udC z+8FWJof6cNY3ps_g+!f= z%sU=kO@MLnU&oP$`U307nC{7$fivMkb(62%&kiu<+^1gzR+Kwq4vJI`@EAG2_ZEtH zwRm~Ux0J}4iaCeVHWlmEb2G;4Haq1rgm%l1Ur(CBy?F3C_ifkBJw0Fi+&^{(zgNuM z6`=9tjJ5KGII}80?Gdr9rKd8}yIU6U&5mkL=EsG6lv)6;u@EQ_T%WRjN=-HhxPs@Y? z$IKnSmNM#IAnpy%eu)+DLEez|Zx4a)aGl{z-+s!s(_USXdFJYky_B0rFQ(Sl!p%SC zq}zRT{kp~~veAN$((6C@5P}+>tGnDF^NN)ZwJAu)*GknFL_KyU9WW~0H@*&e94kBt zoR3M<6%-eGuBF8;Qme%t?2 zr~9r%6udXDRItOBWTHvqTyRvq!|E#Df70s2Lh*h(y(4L|{oSz_QUZ-|T(~Ljm%U-L z@~5g!L@M=Ju{GV)2>6Bn+}Vs*oa=MS*AHGSzHpk-tR+?z-#wQ#aal9}j^3#DOsMko zo4fYhk9C1et0kqFNW%ePvj{U+1z_s2u8i5!>V)zy9AT5f$K)<$&N_*xo?GM;kvMUu z-VknQaAd*!)%>YGaVAz#A}kf3C!tSx$Ypq+|IiNCqr!I&F*>vMN%5t|Nz4j`cjh*; zt8$n0Nw&F?cOM#PC0bYYzaCU~p}0eZr9$6&~+{m$l6rYIeM?=T7WOGPt0g z;@7diTT%Wq`~9NU-eb+w(Lq}`NhCKv`-gljWPm3Y*nOO*z#~9RfDdOVEDZUiL!66Y8tAJjJ}he%27KBS`v%PtzX>|=e#=bFen^slAFFW28X zdkK(Glr&z~B(8L(Kk7_6E;f87_?jE5Ss}d8aD2-c`|CDI>7W*;%b$EC_)ZgqhDID+ z20BMP@bXRdqGE3w&J{R#KFFSS%sAhWu4r$W+i_0*exib+e1FzT=h;i2$>k9xl#@kT z3%H8OMgD~S4H3OHQ!h%e;#F-mPd!5(YxLj_8)M6WY@g@aNP(eumIadzT{>wKG)K$W zcBbeRAlR)qtB)?uTcI$hyoD;Hsw5gI?W7Q(%C0W+lh90 z!C95=0E*|-K3>yq{=(jjBLfMa*bK1PcQGPeNAU%(BtNK_-$|;aMPY-yZmh|}lML}C zw3qKxV3coGp?mplfG(05I42(U+*qLeVnk%#gc4qZH+Xqt**23PAv zWExD5{g!#Uf%|!pp7mMi%OGvzi+O^wm92JPay-n>b%1nR&R4&U5WNB*Y_ zRhkF?)~i`&F-y#%I_}qA$?*+XW@N~=!J=;fM?ReWH?B-yr4{nM*(W*^RmbZ&#uo() zlb?nkRv500>2>}MK6)eF#SGocKtJ4XitMIxhtJU-+{sD>?)zZo{KM1^!mz1@{?>Yy z*Us#El~ULgJSqA@w)M;Fc5^ymsnrkNx6bAYJc}N`Zs)n-yYlG+Oa5C0SA}ha%$#r~;qqgZkB=taaxT5` zNN1V98540d{(~BKY9oi3yuqm`J`LNbGZNRZN@Di6i@R2wf-_qhIBkACs`hHo4>d0?3ais^!!f9(zZjBxZ^ckRuSjC0{osSa^UCtu9&>-k0+~LBGsNZtDaak}xvZc(~%Mumoy=3Z6*y22@x9G_WyYW7F1S(eMO+ zfC=#ie93`2(3!gXPzaf*19efyA#gM!fJ8PAqXQ0M){cZQe*%UG)zjtEW??~q0Dy^y zumUK73@l3rx`~Sge{YE4P{^hV(_aVbinE0nQRx6gO+`%w0W)EdLy%BiK8QA*=!3O4 zHvItsKIuS7OePHrhlhrSs)V9csB~YrDh7jrBam<;5(a9(7)Jw{cor;>p|An*9m5!4 z5a?tYlS~bSY+&NOsliMgC=?ur{O(@>4Tt*!K9KQ)1&|Lo3r~ZqsvzJ20q~zK7)+B8 z5afqL|JH)x2=;Gqdw@X=rV{{@5Fn7L@G}IF@P|Dum`>RYhe&_}6d(XpWq`A){$)xt z3!LpA78?}!k^^X)Rv_7b(PWZ+{v_)!zHN+bhV!!{p!pxTf6@Lu_Dy9_3x~rRQwhNv z?pYY?KsV;c5~&0-5xe;lO+*7I3<`j$5)s}oHM}|+hDUh&z|_zhnwoe`O$4Ck^AnUs zAcKhyBmf&wAh-${#8Jm0;~O5K~FiHD&y)ippU6cL6|1H57C>IffA4Ctv3`a2YnfHkGk1Mpxv z$pLs@08R_^-JD~CaIAr?g$@*{g7{NnOTjaJKm#2p4o`sCIQ%){NDcrTnD`AkRW&p) z7)^CmO|-fiMqLB>r;;;3XMm-+0ji2nL464~f2d*a~o{2ZcGXW43fka|ekyr%M5xlEouqgC?1PY7zNuNq2`yBmG z+8e6}qWwMR=41vq|Itm+_a)^31bu({{zxHjt|bU$b5&sRgzq6R@F4(k(@zlV`w)SI z5A+4V?c+zi{w^o~n_57kQEEg4+8gGBR7Jzodo-_66>S@ISW0AEa$;JpaSjk7WE0X8@u9ndEQr`ww0J(Dk<%_*=^VRM$Ur z{VfLmmhwN<^Z@0Fy15?-3Q8-tBa zMPcJ&XBFv%fI@Dj1aR^)}AP@J?o%Il4;_~G@=GtvO{j9&J>Z|v_y@r;oY$NuW*w}&jJwZ6>l)cF`M4k$gots`r2--j245+u&^v5QIX zCEb)coOsFotYi-NF}rlgy*F3X{pah32PjNlHXQHa?a`$xOYWpsX0d<#9lb|*S;sW<6s~8XZ zwVqMzuWn;T#Sd3EBnd)0Vk=Y@W~j};=fM0DW$?Y4-W&~$X0)xIzNaYyqE=K&*2 zZplDiS7nk#?UeNLR<(V1LW}SDi?3~M+n(<=HvF-CaU1(L{omln(e%KS`p?IqA6K*< zcBTw*pGRSSX~{zwzZ9_Pb^e&J9|B8J048FVTw2!OF7Ljf2NL8C$-5zpRw zx}SZG5&hJ?Ygw>RE!}MG0SJRDH>9v*iXe{=RZMoCQPB($3Pmwi-fc*@wps$m+uaQQ=P z{%1?zMe~A5U*NSD-FGF+$J|Zqc#7WK*}q>))5KCM_3WQvwAap1{UI-Jjpw@3{c(?Y z-(GNUAASBOocChrwk0ahTu|j?IW7sN&xPk-9$s@1xxmuD6SS&l=>Qj5A5taf-k48D z?js)i)YhmiwJK8078=m0I`RH+d}P;~&dIJ}HEOU2W%2f=kGnr;(9`xTk&XqQye4ItbyIoI@E~$1Lc=!jo9@FXXky#}m8>?{jSLN_*cX^A* zyJJ#%4!*&iB5{(2hwkp@D|_Z9LE~Gop{>TMSDe2SP>y(fX}3##%XECjqpbSOc0skB z-Xf>l-&=_&*`x274-^U|R1PK}Y|j#|9B@4pSg(8HH!qWH$zMLdN-(^D;qR7&qzSkU z*l7Pxync|rTkL1NBV7MB+mz!)$1Ii|l zS1#4EkxQy=`nM^VyIul+@hY|#%%s%yVP}seBbsMYCwdkPBlh-Vvwe>X8?wcB6=xJB z>9tIfjb$BBY8RmQ8|@N@q@8WC(pEFcwmRS8>O)r_vsB%O zFId_lTrZCZ!y6tj+?8DZY-oKKJy~0pCs3}}Thk$8#GQ({-;?n8?EYS{9K*b=CUjq6n4E~N5jWwW^HD7FI`i$ zEJ(BxM`p2VL^f~3c?~_E%$v2ab5B_@D3duoSFD>+8Y-rzzA3yqakRt$&*O5BSu_9c zwfE_S9?cyZ0{qgtfOD8|rKk$FA>}uV*=*mFn+uiJx~?9R=32nNSv^}Wlz(0Av3X-a zkZ{pc&BPtW)*d4-8ifZcWcv6r=$jk+5iXtq{{5B(!56f7-*K;~J$!XkFCegFB*rIZ zS|u#WIAlI&Z1~{RP}1}45Qz!3RWaEWtYoH{zSGFW8}3jASmToeVKyVGTf}qV)?-!1 zKJA*t1<8JAiTu7uzWV;jtX&kAY)IzYQ}rWI%u!bQsU+2kHl>!Ra`MhQl%3ZkZ@ak3 z2SSV^P4}1^1H{U%4}kHhqIr^OZr@!H+yT9CuxoOGHN=wTDZybHHGH`YvF$^<6PN{*S#3)A>^Zaz^l_dZq`wKwH&y`yzKfSwMujP zjHgfBt`r}YG-}WMEs~T;T-46%kqW8_+|c>Qo!d(yg{Sc?H<}`L^!P-uNugG?X;KzD z)g?RRwL-*AlYc#tkUVgpSkzRQh#)wdTpXU7conJAF}U^q>9fFz7xlAHY{RT%XiW&; zmg(-btKKHO&sv*ewq8xhWYI+EsdHVu(I(M@3cox!9WFCnpwAYS^PA}_K9nrzwpf6- zzr6OgPP;gvdR}11HAcpx6Px-YM*r9F**@Q&G6! zX1%_&oS4Q{7m8gt{(yX|B*yW<)XUJn_C3nOuGs^_y0JZj6~DiDZa7lExfi;%WW+u0 zc8I1`+^&1$UgO6PScn-s9U2dCEQ1jPv~`FLYfS zm3M&NUkSt`cD^^?94{9+AgDZC%`a=6B1rc8bTN`7m( zYzA$~BiZxB9X!>yTlwny*hlk2MWS~q+@6I%%a;2T8x*3cl~5g16N=h#f=5T5{lg|3 zjU66{b3&Hs2gldn)_#g=SxR=EMOhf7^~t&87=3%fd3+;vw(V&B#paN{L-1u^Y{Hpdd#Ii^K-1m2*MJkQGjhmIM^R};Uw!Z=ByqxUk;Um@uE#8YQ9s$Z6 z%otvV4IhWz>~kKUlKpse?MfN@?1F-P>M>sDXULL#^_}c3pEFL4opnV(i~DzB>?YV} z79J_Nj2?Rrb*^rx?F)$R&Jru@=BbokykvY%XZam+{Gq21fxR#rwB0bquIP*mS3 z0jC3-k50VK*PlTe74W}(f$8IKRGE>T6u`dZ-}<{>HeV+Sa5@;(OcmKCt0T{F6Vrs2BT2|C-FGIe5|a_2@hO*}I;t ztX@B<#vz0IRc)`Mcjw5gkc7kUY317AFfWSZ`>ZjUphXg6BlFj}gv$^0Url|O4S{g6 zX=Y}QHfCmj_k3Wdmm8Cbx9%|B(&gh^vRg(#BN0O>-z#fw!5Z2vU0_-+;}wQ%UNrN+ zpHbX|5vt}BFp{khMfA1cnuLyx2qYGrK3$l6X7xt#fby;5W3ks-SDK;)1g}aCRW|H1 z9tzgZN=(j~IVB%uA{*|f@l7U?4;LHRN&0mBA2zZ z`)$RM5pj8h%gJcj~0Sgo^8FM45Lx-%I>-ZBq7uc@TXZvF#zYNy)NXaKr)u1 zY-A{85P<^$f&ms077-j2!o)@3l{av4;5|pIr3~9JVFluqJqV64Gdcr+=^%6vNVr7= z?J!!|PzYwgpips6=9XU}z$d)2KZ_NL)6xnL4@ZP+Bj^l2Eff}u)k31R&}cYl0cS>r zu!s@x5T*(T;tPg3z$7zhp)49b1jfN6lIUS9ys|Q=hy5L2a43QB4L*eVl?9Lwtq5YM z77BsX3J%u#-h;`qI1GY(4d}mmFkQe`87(J(Ne^R?0gJ;x2utOA2nzX|e`pvZXd@j8 zSqlgPfPsm3zw21JDg%Fu_vffTEBH?JpY6yx_oWKxT=YLInXf zV8LW?W(xn{S5zUD#00c#%(Kr+uheW%ew81M*7pZ{+ zZ@#mqQ)tx4|IM1SdSC`$=G>ab1jmou5Pexv&cLBBPhTE`Xd7z@2HRK_I3oE=3QXc* z;0whd))y7opBUl?fZNB{di`5Y`!BVC(j^0AG#Lfg)uy80I=Vz6SWenlxDJ+z!~zr| zQXh@kVBs4&lTKxY6B&SsAIKxf6{iN$(G4QX9e^%H38C^pEyifrlU>g(;zAhC;?ghVuK=?^^R_2g(&M*J> zveTesQ>e8k69Pe~a89m>5~IVQP=IAaun>3y>AsgKy zK!JtRSpG0hx9EOUR1SRU-fv@W;^P1E?fG`e<1nasYRCFIBg*#{aR+Z==gGr(p{y8o z2)AwTk(nbGy5E2fkobF-U*jPXNh|J8!{48MDS4@JZ^~$9Pqk;inB4KU-o}u95HCiU zXYD_*|04@V1Lu1xX9FZ#|MVzojME#5z{?L0Exq2Rw3M zW>MoVO;2#**@;-i=z$Z3lu?67?}W(Fk7!1>W1-nk>cfG)vy$E2<3O;}qXwUnil-;kax^cc-{dmlkLAJFDp0vqzHD2Us NY%KPgSDX4C`yZHc9FG71 literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..1342dc7bf95265db5d0ab2924e1ef03fe4df9943 GIT binary patch literal 7308 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate new file mode 100644 index 000000000..d8263ee98 --- /dev/null +++ b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/applications/plugins/spi_mem_manager/images/Dip8_10px.png b/applications/plugins/spi_mem_manager/images/Dip8_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..9de9364d13c5cbd7fe43a6df59789193d9133057 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#85cXB6sN>NS&&A_64!_l=ltB<)VvY~=c3falGGH1^30M91$R&1fbd2>aiAhw zPZ!4!iOaqHj$8}|9EV^1`~UonL4lUvGtG!wL5x*034X4|#-7<_>P#}{{_l#8SgPZ+ js&kpz9*+3KCj7Uj_`9EfRa>$PXb^*^tDnm{r-UW|gHJf_ literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/Dip8_32x36.png b/applications/plugins/spi_mem_manager/images/Dip8_32x36.png new file mode 100644 index 0000000000000000000000000000000000000000..8f01af2760363b31029cbc425d9a9f2e2becf96b GIT binary patch literal 977 zcmeAS@N?(olHy`uVBq!ia0vp^3P7yF!3-qto1VD9z`)E9;1l8s2$&H|6fVg?3oVGw3ym^DWNC|K_4;uvDl`*xDGP=kVibG*#E`lk^cO$zev z+}xJ~R3@KooBBeTVTU7E1RI;jy{)QdNiPgUSiTrM5ocMrlj*@Cjo*LH^-f?g|F=lv zl)Fs&L8nKli4M~yCl?8HDNptH;hVjxE12WemhD};j?@R{i;C*6a`dzQvVm!LtxQw_ S>l}Mfba=Y@xvXZB8b0=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/spi_mem_manager/images/DolphinNice_96x59.png b/applications/plugins/spi_mem_manager/images/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!Q8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png b/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c3ce363f801cff5080d10599ca258d07783fa1 GIT binary patch literal 4446 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 != SPIMemChipVendorUnknown && vendor->vendor_enum != vendor_enum) + vendor++; + return vendor->vendor_name; +} + +bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips) { + const SPIMemChip* chip_info_arr; + found_chips_reset(found_chips); + for(chip_info_arr = SPIMemChips; chip_info_arr->model_name != NULL; chip_info_arr++) { + if(chip_info->vendor_id != chip_info_arr->vendor_id) continue; + if(chip_info->type_id != chip_info_arr->type_id) continue; + if(chip_info->capacity_id != chip_info_arr->capacity_id) continue; + found_chips_push_back(found_chips, chip_info_arr); + } + if(found_chips_size(found_chips)) return true; + return false; +} + +void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src) { + memcpy(dest, src, sizeof(SPIMemChip)); +} + +size_t spi_mem_chip_get_size(SPIMemChip* chip) { + return (chip->size); +} + +const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip) { + return (spi_mem_chip_search_vendor_name(chip->vendor_enum)); +} + +const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum) { + return (spi_mem_chip_search_vendor_name(vendor_enum)); +} + +const char* spi_mem_chip_get_model_name(const SPIMemChip* chip) { + return (chip->model_name); +} + +uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip) { + return (chip->vendor_id); +} + +uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip) { + return (chip->type_id); +} + +uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip) { + return (chip->capacity_id); +} + +SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip) { + return (chip->write_mode); +} + +size_t spi_mem_chip_get_page_size(SPIMemChip* chip) { + return (chip->page_size); +} + +uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip) { + return ((uint32_t)chip->vendor_enum); +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h new file mode 100644 index 000000000..683937df4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +typedef struct SPIMemChip SPIMemChip; + +ARRAY_DEF(found_chips, const SPIMemChip*, M_POD_OPLIST) + +typedef enum { + SPIMemChipStatusBusy, + SPIMemChipStatusIdle, + SPIMemChipStatusError +} SPIMemChipStatus; + +typedef enum { + SPIMemChipWriteModeUnknown = 0, + SPIMemChipWriteModePage = (0x01 << 0), + SPIMemChipWriteModeAAIByte = (0x01 << 1), + SPIMemChipWriteModeAAIWord = (0x01 << 2), +} SPIMemChipWriteMode; + +const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip); +const char* spi_mem_chip_get_model_name(const SPIMemChip* chip); +size_t spi_mem_chip_get_size(SPIMemChip* chip); +uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip); +uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip); +uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip); +SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip); +size_t spi_mem_chip_get_page_size(SPIMemChip* chip); +bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips); +void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src); +uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip); +const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c new file mode 100644 index 000000000..25b237d68 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c @@ -0,0 +1,1399 @@ +#include "spi_mem_chip_i.h" +const SPIMemChip SPIMemChips[] = { + {0x1F, 0x40, 0x00, "AT25DN256", 32768, 256, SPIMemChipVendorADESTO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x20, "A25L05PT", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "A25L05PU", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x21, "A25L10PT", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x11, "A25L10PU", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x22, "A25L20PT", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x12, "A25L20PU", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x23, "A25L40PT", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x13, "A25L40PU", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x24, "A25L80PT", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x14, "A25L80PU", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x25, "A25L16PT", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "A25L16PU", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "A25L512", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "A25L010", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "A25L020", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "A25L040", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "A25L080", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "A25L016", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "A25L032", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x15, "A25LQ16", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x16, "A25LQ32A", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x68, 0x40, 0x14, "BY25D80", 1048576, 256, SPIMemChipVendorBoya, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05T", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10T", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20T", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40T", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80T", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16T", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32T", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64T", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25F05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25F10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25F20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25F40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x14, "EN25F80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x15, "EN25F16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x16, "EN25F32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25LF05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25LF10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25LF20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25LF40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25P05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25P10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25P20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25P40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25P80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25P16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25P32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25P64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x13, "EN25Q40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x14, "EN25Q80A", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x15, "EN25Q16A", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32B", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x17, "EN25Q64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x18, "EN25Q128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x15, "EN25QH16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25QH32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x17, "EN25QH64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x18, "EN25QH128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x19, "EN25QH256", 33554432, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x14, "EN25T80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x15, "EN25T16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x17, "EN25F64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7C, "Pm25LV010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x21, "Pm25LD010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x22, "Pm25LV020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7D, "Pm25W020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7E, "Pm25LV040", 524288, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ZP25L16P", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "TS25L16PE", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "TS25L80PE", 1048576, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "TS25L032A", 4194304, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "TS25L40P", 524288, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x10, + "GPR25L005E", + 65536, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "GPR25L161B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x12, + "GPR25L020B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "GPR25L3203F", + 4194304, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "AC25LV512", 65536, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "AC25LV010", 131072, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "EM25LV512", 65536, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "EM25LV010", 131072, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L004A", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L008A", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L016A", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x8C, 0x8C, "F25L04UA", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x30, 0x13, "F25S04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L08P", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L16P", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x16, "F25L32P", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x40, 0x16, "F25L32Q", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x11, "ES25P10", 131072, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x12, "ES25P20", 262144, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x13, "ES25P40", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x14, "ES25P80", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x15, "ES25P16", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x16, "ES25P32", 4194304, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x13, "ES25M40A", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x14, "ES25M80A", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x15, "ES25M16A", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x14, "FM25Q08A", 1048576, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16A", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16B", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x16, "FM25Q32A", 4194304, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x17, "FM25Q64A", 8388608, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x13, "GD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x14, "GD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x13, "GD25F40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x14, "GD25F80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x10, "GD25Q512", 65536, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x11, "GD25Q10", 131072, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x12, "GD25Q20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x12, + "GD25LQ20C_1.8V", + 262144, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x13, "GD25Q40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x14, "GD25Q80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80B", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80C", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x15, "GD25Q16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x15, + "GD25Q16B", + 2097152, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x16, "GD25Q32", 4194304, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x16, + "GD25Q32B", + 4194304, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x17, "GD25Q64", 8388608, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25Q64B", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25B64C", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128B", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128C", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x17, + "GD25LQ064C_1.8V", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x18, + "GD25LQ128C_1.8V", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x19, + "GD25LQ256C_1.8V", + 33554432, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x31, 0x14, "MD25T80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x12, "MD25D20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x13, "MD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x14, "MD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x15, "MD25D16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "ICE25P05", 65536, 128, SPIMemChipVendorICE, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QB25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x13, "QB25F640S33B", 8388608, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QH25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005A", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "KH25L2005", 262144, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005A", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512A", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "KH25L8005", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x26, 0x15, "KH25L8036D", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005A", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x11, "MX25L1021E", 131072, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1025C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1026E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12805D", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12836E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12839F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845G", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12873F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12875F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25635E", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x15, "MX25L1605", 2097152, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605A", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1606E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1633E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1635D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1635E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1636D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1636E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1673E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1675E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x16, "MX25L3205", 4194304, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205A", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3206E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3208E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3225D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3233F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3235D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3235E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3236D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3237D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25L3239E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3275E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005A", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005C", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4026E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512A", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x10, "MX25L5121E", 65536, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x17, "MX25L6405", 8388608, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6405D", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6406E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6408E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6433F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6435E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25L6439E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6445E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6465E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6475E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25L8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8008E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8035E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8036E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8073E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8075E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25673G", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x10, "MX25R512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x11, "MX25R1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x15, + "MX25R1635F", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x12, "MX25R2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x16, + "MX25R3235F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x13, "MX25R4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x17, + "MX25R6435F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x14, + "MX25R8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x31, + "MX25U1001E_1.8V", + 131072, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x18, + "MX25U12835F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25673G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25645G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635E_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635F_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2032E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2033E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235E_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235F_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4032E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4033E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4035_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x30, + "MX25U5121E_1.8V", + 65536, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6435F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6473F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8032E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8033E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x38, + "MX25U12873F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25V1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x11, "MX25V1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25V2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x12, "MX25V2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512E", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x10, "MX25V512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x53, "MX25V4035", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x13, "MX25V4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25V8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25V8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x54, "MX25V8035", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x23, + 0x14, + "MX25V8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3A, + "MX66U51235F_1.8V", + 67108864, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3B, + "MX66U1G45G_1.8V", + 134217728, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x16, "N25Q032A", 4194304, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x17, "N25Q064A", 8388608, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "N25Q256A13", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "N25Q512A83", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x2C, 0xCB, 0x19, "N25W256A11", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x18, + "MT25QL128AB", + 16777216, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "MT25QL256A", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "MT25QL512A", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x22, + "MT25QL02GC", + 268435456, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBB, 0x19, "MT25QU256", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x21, + "N25Q00AA13G", + 134217728, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "MS25X512", 65536, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "MS25X10", 131072, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "MS25X20", 262144, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "MS25X40", 524288, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "MS25X80", 1048576, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "MS25X16", 2097152, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "MS25X32", 4194304, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x11, "N25S10", 131072, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x12, "N25S20", 262144, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x13, "N25S40", 524288, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x15, "N25S16", 2097152, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x16, "N25S32", 4194304, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x14, "N25S80", 1048576, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7C, "NX25P10", 131072, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "NX25P16", 2097152, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7D, "NX25P20", 262144, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "NX25P32", 4194304, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7E, "NX25P40", 524288, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x13, "NX25P80", 1048576, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x20, 0x40, 0x15, "M45PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05", 65536, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05A", 65536, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10", 131072, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10A", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "M25P20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "M25P40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "M25P80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "M25P16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "M25P32", 4194304, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "M25P64", 8388608, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, + 0x20, + 0x18, + "M25P128_ST25P28V6G", + 16777216, + 256, + SPIMemChipVendorNUMONYX, + SPIMemChipWriteModePage}, + {0x20, 0x80, 0x11, "M25PE10", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "M25PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x12, "M25PE20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x13, "M25PE40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "M25PE80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25LF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x49, 0x00, "PCT25VF010A", 131072, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "PCT25VF016B", 2097152, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25VF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x4A, "PCT25VF032B", 4194304, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x44, 0x00, "PCT25VF040A", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "PCT25VF040B", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8E, "PCT25VF080B", 1048576, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x10, "S25FL001D", 131072, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x11, "S25FL002D", 262144, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004D", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "S25FL004K", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008A", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008D", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "S25FL008K", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x14, "S25FL016A", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "S25FL016K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032A", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "S25FL032K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032P", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL040A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x26, + "S25FL040A_BOT", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x25, + "S25FL040A_TOP", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064A", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "S25FL064K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064P", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x15, "S25FL116K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, + 0x40, + 0x18, + "S25FL128K", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128P", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128S", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x40, 0x16, "S25FL132K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x17, "S25FL164K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x19, + "S25FL256S", + 33554432, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "SST25VF016B", 2097152, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8C, "SST25VF020B", 262144, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4A, "SST25VF032B", 4194304, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4B, "SST25VF064C", 8388608, 256, SPIMemChipVendorSST, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "SST25VF040B", 524288, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8E, "SST25VF080B", 1048576, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0x20, 0x71, 0x15, "M25PX16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x16, "M25PX32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x17, "M25PX64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x14, "M25PX80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05", 65536, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05A", 65536, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10", 131072, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10A", 131072, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ST25P16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "ST25P20", 262144, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "ST25P32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "ST25P40", 524288, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "ST25P64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "ST25P80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0xEF, 0x10, 0x00, "W25P10", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "W25P16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x11, 0x00, "W25P20", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "W25P32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x12, 0x00, "W25P40", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x17, "W25P64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x14, "W25P80", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x11, + "W25Q10EW_1.8V", + 131072, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128BV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128FV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x18, "W25Q128JV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256FV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x18, + "W25Q128FW_1.8V", + 16777216, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16DV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x15, + "W25Q16FW_1.8V", + 2097152, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x12, "W25Q20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x12, + "W25Q20EW_1.8V", + 262144, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32FV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x16, + "W25Q32FW_1.8V", + 4194304, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x13, + "W25Q40EW_1.8V", + 524288, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64CV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64FV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64JV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x17, + "W25Q64FW_1.8V", + 8388608, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x50, + 0x14, + "W25Q80BW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80DV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x14, + "W25Q80EW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05CL", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10AV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10CL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10L", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10V", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20L", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20V", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32AV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40L", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40V", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64V", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80L", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80V", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x71, 0x19, "W25M512JV", 67108864, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25R256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "TS25L16P", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x5E, 0x40, 0x15, "ZB25D16", 2097152, 256, SPIMemChipVendorZbit, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "BG25Q40A", 524288, 256, SPIMemChipVendorBerg_Micro, SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x14, + "BG25Q80A", + 1048576, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x15, + "BG25Q16A", + 2097152, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x16, + "BG25Q32A", + 4194304, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0x1F, 0x23, 0x00, "AT45DB021D", 270336, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x24, 0x00, "AT45DB041D", 540672, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x26, 0x00, "AT45DB161D", 2162688, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x27, 0x01, "AT45DB321D", 4325376, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x43, 0x00, "AT25DF021", 262144, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041A", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x84, 0x00, "AT25SF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT25DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x48, 0x00, "AT25DF641", 8388608, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x65, 0x00, "AT25F512B", 65536, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161A", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x04, 0x00, "AT26F004", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0xE0, + 0x60, + 0x18, + "ACE25A128G_1.8V", + 16777216, + 256, + SPIMemChipVendorACE, + SPIMemChipWriteModePage}, + {0x9B, 0x32, 0x16, "ATO25Q32", 4194304, 256, SPIMemChipVendorATO, SPIMemChipWriteModePage}, + {0x54, 0x40, 0x17, "DQ25Q64A", 8388608, 256, SPIMemChipVendorDOUQI, SPIMemChipWriteModePage}, + {0x0E, 0x40, 0x15, "FT25H16", 2097152, 256, SPIMemChipVendorFremont, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x13, "FM25Q04A", 524288, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x16, "FM25Q32", 4194304, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x14, "GT25Q80A", 1048576, 256, SPIMemChipVendorGenitop, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "PN25F04A", 524288, 256, SPIMemChipVendorParagon, SPIMemChipWriteModePage}}; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h new file mode 100644 index 000000000..30d607094 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include "spi_mem_chip.h" + +typedef enum { + SPIMemChipVendorUnknown, + SPIMemChipVendorADESTO, + SPIMemChipVendorAMIC, + SPIMemChipVendorBoya, + SPIMemChipVendorEON, + SPIMemChipVendorPFLASH, + SPIMemChipVendorTERRA, + SPIMemChipVendorGeneralplus, + SPIMemChipVendorDEUTRON, + SPIMemChipVendorEFST, + SPIMemChipVendorEXCELSEMI, + SPIMemChipVendorFIDELIX, + SPIMemChipVendorGIGADEVICE, + SPIMemChipVendorICE, + SPIMemChipVendorINTEL, + SPIMemChipVendorKHIC, + SPIMemChipVendorMACRONIX, + SPIMemChipVendorMICRON, + SPIMemChipVendorMSHINE, + SPIMemChipVendorNANTRONICS, + SPIMemChipVendorNEXFLASH, + SPIMemChipVendorNUMONYX, + SPIMemChipVendorPCT, + SPIMemChipVendorSPANSION, + SPIMemChipVendorSST, + SPIMemChipVendorST, + SPIMemChipVendorWINBOND, + SPIMemChipVendorZEMPRO, + SPIMemChipVendorZbit, + SPIMemChipVendorBerg_Micro, + SPIMemChipVendorATMEL, + SPIMemChipVendorACE, + SPIMemChipVendorATO, + SPIMemChipVendorDOUQI, + SPIMemChipVendorFremont, + SPIMemChipVendorFudan, + SPIMemChipVendorGenitop, + SPIMemChipVendorParagon +} SPIMemChipVendor; + +typedef enum { + SPIMemChipCMDReadJEDECChipID = 0x9F, + SPIMemChipCMDReadData = 0x03, + SPIMemChipCMDChipErase = 0xC7, + SPIMemChipCMDWriteEnable = 0x06, + SPIMemChipCMDWriteDisable = 0x04, + SPIMemChipCMDReadStatus = 0x05, + SPIMemChipCMDWriteData = 0x02, + SPIMemChipCMDReleasePowerDown = 0xAB +} SPIMemChipCMD; + +enum SPIMemChipStatusBit { + SPIMemChipStatusBitBusy = (0x01 << 0), + SPIMemChipStatusBitWriteEnabled = (0x01 << 1), + SPIMemChipStatusBitBitProtection1 = (0x01 << 2), + SPIMemChipStatusBitBitProtection2 = (0x01 << 3), + SPIMemChipStatusBitBitProtection3 = (0x01 << 4), + SPIMemChipStatusBitTopBottomProtection = (0x01 << 5), + SPIMemChipStatusBitSectorProtect = (0x01 << 6), + SPIMemChipStatusBitRegisterProtect = (0x01 << 7) +}; + +typedef struct { + const char* vendor_name; + SPIMemChipVendor vendor_enum; +} SPIMemChipVendorName; + +struct SPIMemChip { + uint8_t vendor_id; + uint8_t type_id; + uint8_t capacity_id; + const char* model_name; + size_t size; + size_t page_size; + SPIMemChipVendor vendor_enum; + SPIMemChipWriteMode write_mode; +}; + +extern const SPIMemChip SPIMemChips[]; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c new file mode 100644 index 000000000..3518ca25c --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c @@ -0,0 +1,152 @@ +#include +#include +#include "spi_mem_chip_i.h" +#include "spi_mem_tools.h" + +static uint8_t spi_mem_tools_addr_to_byte_arr(uint32_t addr, uint8_t* cmd) { + uint8_t len = 3; // TODO(add support of 4 bytes address mode) + for(uint8_t i = 0; i < len; i++) { + cmd[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF; + } + return len; +} + +static bool spi_mem_tools_trx( + SPIMemChipCMD cmd, + uint8_t* tx_buf, + size_t tx_size, + uint8_t* rx_buf, + size_t rx_size) { + bool success = false; + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + do { + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, (uint8_t*)&cmd, 1, SPI_MEM_SPI_TIMEOUT)) + break; + if(tx_buf) { + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, tx_buf, tx_size, SPI_MEM_SPI_TIMEOUT)) + break; + } + if(rx_buf) { + if(!furi_hal_spi_bus_rx( + &furi_hal_spi_bus_handle_external, rx_buf, rx_size, SPI_MEM_SPI_TIMEOUT)) + break; + } + success = true; + } while(0); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + return success; +} + +static bool spi_mem_tools_write_buffer(uint8_t* data, size_t size, size_t offset) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + uint8_t cmd = (uint8_t)SPIMemChipCMDWriteData; + uint8_t address[4]; + uint8_t address_size = spi_mem_tools_addr_to_byte_arr(offset, address); + bool success = false; + do { + if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, &cmd, 1, SPI_MEM_SPI_TIMEOUT)) + break; + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, address, address_size, SPI_MEM_SPI_TIMEOUT)) + break; + if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, data, size, SPI_MEM_SPI_TIMEOUT)) + break; + success = true; + } while(0); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + return success; +} + +bool spi_mem_tools_read_chip_info(SPIMemChip* chip) { + uint8_t rx_buf[3] = {0, 0, 0}; + do { + if(!spi_mem_tools_trx(SPIMemChipCMDReadJEDECChipID, NULL, 0, rx_buf, 3)) break; + if(rx_buf[0] == 0 || rx_buf[0] == 255) break; + chip->vendor_id = rx_buf[0]; + chip->type_id = rx_buf[1]; + chip->capacity_id = rx_buf[2]; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_check_chip_info(SPIMemChip* chip) { + SPIMemChip new_chip_info; + spi_mem_tools_read_chip_info(&new_chip_info); + do { + if(chip->vendor_id != new_chip_info.vendor_id) break; + if(chip->type_id != new_chip_info.type_id) break; + if(chip->capacity_id != new_chip_info.capacity_id) break; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { + if(!spi_mem_tools_check_chip_info(chip)) return false; + for(size_t i = 0; i < block_size; i += SPI_MEM_MAX_BLOCK_SIZE) { + uint8_t cmd[4]; + if((offset + SPI_MEM_MAX_BLOCK_SIZE) > chip->size) return false; + if(!spi_mem_tools_trx( + SPIMemChipCMDReadData, + cmd, + spi_mem_tools_addr_to_byte_arr(offset, cmd), + data, + SPI_MEM_MAX_BLOCK_SIZE)) + return false; + offset += SPI_MEM_MAX_BLOCK_SIZE; + data += SPI_MEM_MAX_BLOCK_SIZE; + } + return true; +} + +size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip) { + UNUSED(chip); + return (SPI_MEM_FILE_BUFFER_SIZE); +} + +SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip) { + UNUSED(chip); + uint8_t status; + if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) + return SPIMemChipStatusError; + if(status & SPIMemChipStatusBitBusy) return SPIMemChipStatusBusy; + return SPIMemChipStatusIdle; +} + +static bool spi_mem_tools_set_write_enabled(SPIMemChip* chip, bool enable) { + UNUSED(chip); + uint8_t status; + SPIMemChipCMD cmd = SPIMemChipCMDWriteDisable; + if(enable) cmd = SPIMemChipCMDWriteEnable; + do { + if(!spi_mem_tools_trx(cmd, NULL, 0, NULL, 0)) break; + if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) break; + if(!(status & SPIMemChipStatusBitWriteEnabled) && enable) break; + if((status & SPIMemChipStatusBitWriteEnabled) && !enable) break; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_erase_chip(SPIMemChip* chip) { + do { + if(!spi_mem_tools_set_write_enabled(chip, true)) break; + if(!spi_mem_tools_trx(SPIMemChipCMDChipErase, NULL, 0, NULL, 0)) break; + return true; + } while(0); + return true; +} + +bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { + do { + if(!spi_mem_tools_check_chip_info(chip)) break; + if(!spi_mem_tools_set_write_enabled(chip, true)) break; + if((offset + block_size) > chip->size) break; + if(!spi_mem_tools_write_buffer(data, block_size, offset)) break; + return true; + } while(0); + return false; +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h new file mode 100644 index 000000000..ad006b8ff --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h @@ -0,0 +1,14 @@ +#pragma once + +#include "spi_mem_chip.h" + +#define SPI_MEM_SPI_TIMEOUT 1000 +#define SPI_MEM_MAX_BLOCK_SIZE 256 +#define SPI_MEM_FILE_BUFFER_SIZE 4096 + +bool spi_mem_tools_read_chip_info(SPIMemChip* chip); +bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); +size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip); +SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip); +bool spi_mem_tools_erase_chip(SPIMemChip* chip); +bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c new file mode 100644 index 000000000..438f338f1 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c @@ -0,0 +1,129 @@ +#include "spi_mem_worker_i.h" + +typedef enum { + SPIMemEventStopThread = (1 << 0), + SPIMemEventChipDetect = (1 << 1), + SPIMemEventRead = (1 << 2), + SPIMemEventVerify = (1 << 3), + SPIMemEventErase = (1 << 4), + SPIMemEventWrite = (1 << 5), + SPIMemEventAll = + (SPIMemEventStopThread | SPIMemEventChipDetect | SPIMemEventRead | SPIMemEventVerify | + SPIMemEventErase | SPIMemEventWrite) +} SPIMemEventEventType; + +static int32_t spi_mem_worker_thread(void* thread_context); + +SPIMemWorker* spi_mem_worker_alloc() { + SPIMemWorker* worker = malloc(sizeof(SPIMemWorker)); + worker->callback = NULL; + worker->thread = furi_thread_alloc(); + worker->mode_index = SPIMemWorkerModeIdle; + furi_thread_set_name(worker->thread, "SPIMemWorker"); + furi_thread_set_callback(worker->thread, spi_mem_worker_thread); + furi_thread_set_context(worker->thread, worker); + furi_thread_set_stack_size(worker->thread, 10240); + return worker; +} + +void spi_mem_worker_free(SPIMemWorker* worker) { + furi_thread_free(worker->thread); + free(worker); +} + +bool spi_mem_worker_check_for_stop(SPIMemWorker* worker) { + UNUSED(worker); + uint32_t flags = furi_thread_flags_get(); + return (flags & SPIMemEventStopThread); +} + +static int32_t spi_mem_worker_thread(void* thread_context) { + SPIMemWorker* worker = thread_context; + while(true) { + uint32_t flags = furi_thread_flags_wait(SPIMemEventAll, FuriFlagWaitAny, FuriWaitForever); + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(flags & SPIMemEventStopThread) break; + if(flags & SPIMemEventChipDetect) worker->mode_index = SPIMemWorkerModeChipDetect; + if(flags & SPIMemEventRead) worker->mode_index = SPIMemWorkerModeRead; + if(flags & SPIMemEventVerify) worker->mode_index = SPIMemWorkerModeVerify; + if(flags & SPIMemEventErase) worker->mode_index = SPIMemWorkerModeErase; + if(flags & SPIMemEventWrite) worker->mode_index = SPIMemWorkerModeWrite; + if(spi_mem_worker_modes[worker->mode_index].process) { + spi_mem_worker_modes[worker->mode_index].process(worker); + } + worker->mode_index = SPIMemWorkerModeIdle; + } + } + return 0; +} + +void spi_mem_worker_start_thread(SPIMemWorker* worker) { + furi_thread_start(worker->thread); +} + +void spi_mem_worker_stop_thread(SPIMemWorker* worker) { + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventStopThread); + furi_thread_join(worker->thread); +} + +void spi_mem_worker_chip_detect_start( + SPIMemChip* chip_info, + found_chips_t* found_chips, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + worker->found_chips = found_chips; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventChipDetect); +} + +void spi_mem_worker_read_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventRead); +} + +void spi_mem_worker_verify_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventVerify); +} + +void spi_mem_worker_erase_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventErase); +} + +void spi_mem_worker_write_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventWrite); +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h new file mode 100644 index 000000000..c3761cd5a --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "spi_mem_chip.h" + +typedef struct SPIMemWorker SPIMemWorker; + +typedef struct { + void (*const process)(SPIMemWorker* worker); +} SPIMemWorkerModeType; + +typedef enum { + SPIMemCustomEventWorkerChipIdentified, + SPIMemCustomEventWorkerChipUnknown, + SPIMemCustomEventWorkerBlockReaded, + SPIMemCustomEventWorkerChipFail, + SPIMemCustomEventWorkerFileFail, + SPIMemCustomEventWorkerDone, + SPIMemCustomEventWorkerVerifyFail, +} SPIMemCustomEventWorker; + +typedef void (*SPIMemWorkerCallback)(void* context, SPIMemCustomEventWorker event); + +SPIMemWorker* spi_mem_worker_alloc(); +void spi_mem_worker_free(SPIMemWorker* worker); +void spi_mem_worker_start_thread(SPIMemWorker* worker); +void spi_mem_worker_stop_thread(SPIMemWorker* worker); +bool spi_mem_worker_check_for_stop(SPIMemWorker* worker); +void spi_mem_worker_chip_detect_start( + SPIMemChip* chip_info, + found_chips_t* found_chips, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_read_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_verify_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_erase_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_write_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h new file mode 100644 index 000000000..43e2d2287 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h @@ -0,0 +1,24 @@ +#pragma once + +#include "spi_mem_worker.h" + +typedef enum { + SPIMemWorkerModeIdle, + SPIMemWorkerModeChipDetect, + SPIMemWorkerModeRead, + SPIMemWorkerModeVerify, + SPIMemWorkerModeErase, + SPIMemWorkerModeWrite +} SPIMemWorkerMode; + +struct SPIMemWorker { + SPIMemChip* chip_info; + found_chips_t* found_chips; + SPIMemWorkerMode mode_index; + SPIMemWorkerCallback callback; + void* cb_ctx; + FuriThread* thread; + FuriString* file_name; +}; + +extern const SPIMemWorkerModeType spi_mem_worker_modes[]; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c new file mode 100644 index 000000000..a393e5490 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c @@ -0,0 +1,214 @@ +#include "spi_mem_worker_i.h" +#include "spi_mem_chip.h" +#include "spi_mem_tools.h" +#include "../../spi_mem_files.h" + +static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker); +static void spi_mem_worker_read_process(SPIMemWorker* worker); +static void spi_mem_worker_verify_process(SPIMemWorker* worker); +static void spi_mem_worker_erase_process(SPIMemWorker* worker); +static void spi_mem_worker_write_process(SPIMemWorker* worker); + +const SPIMemWorkerModeType spi_mem_worker_modes[] = { + [SPIMemWorkerModeIdle] = {.process = NULL}, + [SPIMemWorkerModeChipDetect] = {.process = spi_mem_worker_chip_detect_process}, + [SPIMemWorkerModeRead] = {.process = spi_mem_worker_read_process}, + [SPIMemWorkerModeVerify] = {.process = spi_mem_worker_verify_process}, + [SPIMemWorkerModeErase] = {.process = spi_mem_worker_erase_process}, + [SPIMemWorkerModeWrite] = {.process = spi_mem_worker_write_process}}; + +static void spi_mem_worker_run_callback(SPIMemWorker* worker, SPIMemCustomEventWorker event) { + if(worker->callback) { + worker->callback(worker->cb_ctx, event); + } +} + +static bool spi_mem_worker_await_chip_busy(SPIMemWorker* worker) { + while(true) { + furi_delay_tick(10); // to give some time to OS + if(spi_mem_worker_check_for_stop(worker)) return true; + SPIMemChipStatus chip_status = spi_mem_tools_get_chip_status(worker->chip_info); + if(chip_status == SPIMemChipStatusError) return false; + if(chip_status == SPIMemChipStatusBusy) continue; + return true; + } +} + +static size_t spi_mem_worker_modes_get_total_size(SPIMemWorker* worker) { + size_t chip_size = spi_mem_chip_get_size(worker->chip_info); + size_t file_size = spi_mem_file_get_size(worker->cb_ctx); + size_t total_size = chip_size; + if(chip_size > file_size) total_size = file_size; + return total_size; +} + +// ChipDetect +static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event; + while(!spi_mem_tools_read_chip_info(worker->chip_info)) { + furi_delay_tick(10); // to give some time to OS + if(spi_mem_worker_check_for_stop(worker)) return; + } + if(spi_mem_chip_find_all(worker->chip_info, *worker->found_chips)) { + event = SPIMemCustomEventWorkerChipIdentified; + } else { + event = SPIMemCustomEventWorkerChipUnknown; + } + spi_mem_worker_run_callback(worker, event); +} + +// Read +static bool spi_mem_worker_read(SPIMemWorker* worker, SPIMemCustomEventWorker* event) { + uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; + size_t chip_size = spi_mem_chip_get_size(worker->chip_info); + size_t offset = 0; + bool success = true; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= chip_size) break; + if((offset + block_size) > chip_size) block_size = chip_size - offset; + if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer, block_size)) { + *event = SPIMemCustomEventWorkerChipFail; + success = false; + break; + } + if(!spi_mem_file_write_block(worker->cb_ctx, data_buffer, block_size)) { + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + if(success) *event = SPIMemCustomEventWorkerDone; + return success; +} + +static void spi_mem_worker_read_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_file_create_open(worker->cb_ctx)) break; + if(!spi_mem_worker_read(worker, &event)) break; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} + +// Verify +static bool + spi_mem_worker_verify(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { + uint8_t data_buffer_chip[SPI_MEM_FILE_BUFFER_SIZE]; + uint8_t data_buffer_file[SPI_MEM_FILE_BUFFER_SIZE]; + size_t offset = 0; + bool success = true; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= total_size) break; + if((offset + block_size) > total_size) block_size = total_size - offset; + if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer_chip, block_size)) { + *event = SPIMemCustomEventWorkerChipFail; + success = false; + break; + } + if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer_file, block_size)) { + success = false; + break; + } + if(memcmp(data_buffer_chip, data_buffer_file, block_size) != 0) { + *event = SPIMemCustomEventWorkerVerifyFail; + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + if(success) *event = SPIMemCustomEventWorkerDone; + return success; +} + +static void spi_mem_worker_verify_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; + size_t total_size = spi_mem_worker_modes_get_total_size(worker); + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_file_open(worker->cb_ctx)) break; + if(!spi_mem_worker_verify(worker, total_size, &event)) break; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} + +// Erase +static void spi_mem_worker_erase_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_tools_erase_chip(worker->chip_info)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + event = SPIMemCustomEventWorkerDone; + } while(0); + spi_mem_worker_run_callback(worker, event); +} + +// Write +static bool spi_mem_worker_write_block_by_page( + SPIMemWorker* worker, + size_t offset, + uint8_t* data, + size_t block_size, + size_t page_size) { + for(size_t i = 0; i < block_size; i += page_size) { + if(!spi_mem_worker_await_chip_busy(worker)) return false; + if(!spi_mem_tools_write_bytes(worker->chip_info, offset, data, page_size)) return false; + offset += page_size; + data += page_size; + } + return true; +} + +static bool + spi_mem_worker_write(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { + bool success = true; + uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; + size_t page_size = spi_mem_chip_get_page_size(worker->chip_info); + size_t offset = 0; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= total_size) break; + if((offset + block_size) > total_size) block_size = total_size - offset; + if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer, block_size)) { + *event = SPIMemCustomEventWorkerFileFail; + success = false; + break; + } + if(!spi_mem_worker_write_block_by_page( + worker, offset, data_buffer, block_size, page_size)) { + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + return success; +} + +static void spi_mem_worker_write_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; + size_t total_size = + spi_mem_worker_modes_get_total_size(worker); // need to be executed before opening file + do { + if(!spi_mem_file_open(worker->cb_ctx)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_worker_write(worker, total_size, &event)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + event = SPIMemCustomEventWorkerDone; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c new file mode 100644 index 000000000..7780005f4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c @@ -0,0 +1,30 @@ +#include "spi_mem_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const spi_mem_on_enter_handlers[])(void*) = { +#include "spi_mem_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 spi_mem_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "spi_mem_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 spi_mem_on_exit_handlers[])(void* context) = { +#include "spi_mem_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers spi_mem_scene_handlers = { + .on_enter_handlers = spi_mem_on_enter_handlers, + .on_event_handlers = spi_mem_on_event_handlers, + .on_exit_handlers = spi_mem_on_exit_handlers, + .scene_num = SPIMemSceneNum, +}; diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h new file mode 100644 index 000000000..2ac6d21e3 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SPIMemScene##id, +typedef enum { +#include "spi_mem_scene_config.h" + SPIMemSceneNum, +} SPIMemScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers spi_mem_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "spi_mem_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 "spi_mem_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 "spi_mem_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c new file mode 100644 index 000000000..dc0cc4fe4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -0,0 +1,42 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +#define SPI_MEM_VERSION_APP "0.1.0" +#define SPI_MEM_DEVELOPER "DrunkBatya" +#define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" +#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" +#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" + +void spi_mem_scene_about_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* tmp_string = furi_string_alloc(); + + widget_add_text_box_element( + app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false); + widget_add_text_box_element( + app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false); + furi_string_printf(tmp_string, "\e#%s\n", "Information"); + furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP); + furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER); + furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB); + furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); + furi_string_cat_printf( + tmp_string, + "SPI memory dumper\n" + "Originally written by Hedger, ghettorce and x893 at\n" + "Flipper Hackathon 2021\n\n"); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(tmp_string)); + + furi_string_free(tmp_string); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void spi_mem_scene_about_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c new file mode 100644 index 000000000..d9b8f0aa3 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c @@ -0,0 +1,37 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_chip_detect_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_chip_detect_on_enter(void* context) { + SPIMemApp* app = context; + notification_message(app->notifications, &sequence_blink_start_yellow); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewDetect); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_chip_detect_start( + app->chip_info, &app->found_chips, app->worker, spi_mem_scene_chip_detect_callback, app); +} + +bool spi_mem_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventWorkerChipIdentified) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, 0); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectVendor); + } else if(event.event == SPIMemCustomEventWorkerChipUnknown) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetectFail); + } + } + return success; +} + +void spi_mem_scene_chip_detect_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c new file mode 100644 index 000000000..876a28721 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c @@ -0,0 +1,57 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +static void spi_mem_scene_chip_detect_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_chip_detect_fail_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* str = furi_string_alloc(); + widget_add_button_element( + app->widget, + GuiButtonTypeCenter, + "Retry", + spi_mem_scene_chip_detect_fail_widget_callback, + app); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected"); + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignBottom, FontPrimary, "unknown SPI chip"); + furi_string_printf(str, "Vendor\nid: 0x%02X", spi_mem_chip_get_vendor_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 16, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_printf(str, "Type\nid: 0x%02X", spi_mem_chip_get_type_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 64, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_printf(str, "Capacity\nid: 0x%02X", spi_mem_chip_get_capacity_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 110, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_free(str); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_chip_detect_fail_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_previous_scene(app->scene_manager); + } + } + return success; +} +void spi_mem_scene_chip_detect_fail_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c new file mode 100644 index 000000000..539578a45 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c @@ -0,0 +1,94 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_chip_detected_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static void spi_mem_scene_chip_detected_print_chip_info(Widget* widget, SPIMemChip* chip_info) { + FuriString* tmp_string = furi_string_alloc(); + widget_add_string_element( + widget, + 40, + 12, + AlignLeft, + AlignTop, + FontSecondary, + spi_mem_chip_get_vendor_name(chip_info)); + widget_add_string_element( + widget, 40, 20, AlignLeft, AlignTop, FontSecondary, spi_mem_chip_get_model_name(chip_info)); + furi_string_printf(tmp_string, "Size: %zu KB", spi_mem_chip_get_size(chip_info) / 1024); + widget_add_string_element( + widget, 40, 28, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void spi_mem_scene_chip_detect_draw_next_button(SPIMemApp* app) { + FuriString* str = furi_string_alloc(); + if(app->mode == SPIMemModeRead) furi_string_printf(str, "%s", "Read"); + if(app->mode == SPIMemModeWrite) furi_string_printf(str, "%s", "Write"); + if(app->mode == SPIMemModeErase) furi_string_printf(str, "%s", "Erase"); + if(app->mode == SPIMemModeCompare) furi_string_printf(str, "%s", "Check"); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + furi_string_get_cstr(str), + spi_mem_scene_chip_detected_widget_callback, + app); + furi_string_free(str); +} + +static void spi_mem_scene_chip_detected_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) + scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +static void spi_mem_scene_chip_detected_set_next_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeRead) scene = SPIMemSceneReadFilename; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneErase; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneErase; + if(app->mode == SPIMemModeCompare) scene = SPIMemSceneVerify; + scene_manager_next_scene(app->scene_manager, scene); +} + +void spi_mem_scene_chip_detected_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Retry", spi_mem_scene_chip_detected_widget_callback, app); + spi_mem_scene_chip_detect_draw_next_button(app); + widget_add_icon_element(app->widget, 0, 12, &I_Dip8_32x36); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected SPI chip"); + spi_mem_scene_chip_detected_print_chip_info(app->widget, app->chip_info); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_chip_detected_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_chip_detected_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == GuiButtonTypeRight) { + spi_mem_scene_chip_detected_set_next_scene(app); + } + } + return success; +} +void spi_mem_scene_chip_detected_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c new file mode 100644 index 000000000..ca4b765a2 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c @@ -0,0 +1,52 @@ +#include "../spi_mem_app_i.h" + +static void + spi_mem_scene_chip_error_widget_callback(GuiButtonType result, InputType type, void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_chip_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_chip_error_widget_callback, app); + widget_add_string_element( + app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "SPI chip error"); + widget_add_string_multiline_element( + app->widget, + 85, + 52, + AlignCenter, + AlignBottom, + FontSecondary, + "Error while\ncommunicating\nwith chip"); + widget_add_icon_element(app->widget, 5, 6, &I_Dip8_32x36); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +static void spi_mem_scene_chip_error_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneChipDetect; + if(app->mode == SPIMemModeRead || app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_chip_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_chip_error_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + spi_mem_scene_chip_error_set_previous_scene(app); + } + } + return success; +} +void spi_mem_scene_chip_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h new file mode 100644 index 000000000..c0e377303 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h @@ -0,0 +1,21 @@ +ADD_SCENE(spi_mem, start, Start) +ADD_SCENE(spi_mem, chip_detect, ChipDetect) +ADD_SCENE(spi_mem, chip_detected, ChipDetected) +ADD_SCENE(spi_mem, chip_detect_fail, ChipDetectFail) +ADD_SCENE(spi_mem, select_file, SelectFile) +ADD_SCENE(spi_mem, saved_file_menu, SavedFileMenu) +ADD_SCENE(spi_mem, read, Read) +ADD_SCENE(spi_mem, read_filename, ReadFilename) +ADD_SCENE(spi_mem, delete_confirm, DeleteConfirm) +ADD_SCENE(spi_mem, success, Success) +ADD_SCENE(spi_mem, about, About) +ADD_SCENE(spi_mem, verify, Verify) +ADD_SCENE(spi_mem, file_info, FileInfo) +ADD_SCENE(spi_mem, erase, Erase) +ADD_SCENE(spi_mem, chip_error, ChipError) +ADD_SCENE(spi_mem, verify_error, VerifyError) +ADD_SCENE(spi_mem, write, Write) +ADD_SCENE(spi_mem, storage_error, StorageError) +ADD_SCENE(spi_mem, select_vendor, SelectVendor) +ADD_SCENE(spi_mem, select_model, SelectModel) +ADD_SCENE(spi_mem, wiring, Wiring) diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c new file mode 100644 index 000000000..bb5142452 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c @@ -0,0 +1,62 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +static void spi_mem_scene_delete_confirm_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_delete_confirm_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* file_name = furi_string_alloc(); + FuriString* message = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); + widget_add_text_box_element( + app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true); + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "Cancel", + spi_mem_scene_delete_confirm_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Delete", + spi_mem_scene_delete_confirm_widget_callback, + app); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); + furi_string_free(file_name); + furi_string_free(message); +} + +bool spi_mem_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeRight) { + app->mode = SPIMemModeDelete; + if(spi_mem_file_delete(app)) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneSavedFileMenu); + } + } + return success; +} + +void spi_mem_scene_delete_confirm_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c new file mode 100644 index 000000000..0d3ae66bf --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c @@ -0,0 +1,65 @@ +#include "../spi_mem_app_i.h" + +static void + spi_mem_scene_erase_widget_callback(GuiButtonType result, InputType type, void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static void spi_mem_scene_erase_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_erase_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Cancel", spi_mem_scene_erase_widget_callback, app); + widget_add_string_element( + app->widget, 64, 15, AlignCenter, AlignBottom, FontPrimary, "Erasing SPI chip"); + widget_add_string_element( + app->widget, 64, 27, AlignCenter, AlignBottom, FontSecondary, "Please be patient"); + notification_message(app->notifications, &sequence_blink_start_magenta); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_erase_start(app->chip_info, app->worker, spi_mem_scene_erase_callback, app); +} + +static void spi_mem_scene_erase_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +static void spi_mem_scene_erase_set_next_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneSuccess; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneWrite; + scene_manager_next_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_erase_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_erase_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(app->scene_manager); + } else if(event.event == SPIMemCustomEventWorkerDone) { + spi_mem_scene_erase_set_next_scene(app); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } + } + return success; +} +void spi_mem_scene_erase_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + notification_message(app->notifications, &sequence_blink_stop); + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c new file mode 100644 index 000000000..687f17f81 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c @@ -0,0 +1,29 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_file_info_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* str = furi_string_alloc(); + furi_string_printf(str, "Size: %zu KB", spi_mem_file_get_size(app) / 1024); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "File info"); + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_free(str); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_file_info_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneSavedFileMenu); + } + return success; +} +void spi_mem_scene_file_info_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c new file mode 100644 index 000000000..bbf38a303 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c @@ -0,0 +1,57 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_read_progress_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); +} + +static void spi_mem_scene_read_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_read_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_read_callback( + app->view_progress, spi_mem_scene_read_progress_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_blue); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_read_start(app->chip_info, app->worker, spi_mem_scene_read_callback, app); +} + +bool spi_mem_scene_read_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewReadCancel) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } + return success; +} +void spi_mem_scene_read_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c new file mode 100644 index 000000000..4b16baa2e --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c @@ -0,0 +1,46 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_read_filename_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventTextEditResult); +} + +void spi_mem_scene_read_set_random_filename(SPIMemApp* app) { + if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { + size_t filename_start = furi_string_search_rchar(app->file_path, '/'); + furi_string_left(app->file_path, filename_start); + } + set_random_name(app->text_buffer, SPI_MEM_TEXT_BUFFER_SIZE); +} + +void spi_mem_scene_read_filename_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_scene_read_set_random_filename(app); + text_input_set_header_text(app->text_input, "Name the dump"); + text_input_set_result_callback( + app->text_input, + spi_mem_scene_read_filename_view_result_callback, + app, + app->text_buffer, + SPI_MEM_FILE_NAME_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewTextInput); +} + +bool spi_mem_scene_read_filename_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventTextEditResult) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneRead); + } + } + return success; +} +void spi_mem_scene_read_filename_on_exit(void* context) { + SPIMemApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c new file mode 100644 index 000000000..d5767455e --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c @@ -0,0 +1,76 @@ +#include "../spi_mem_app_i.h" + +typedef enum { + SPIMemSceneSavedFileMenuSubmenuIndexWrite, + SPIMemSceneSavedFileMenuSubmenuIndexCompare, + SPIMemSceneSavedFileMenuSubmenuIndexInfo, + SPIMemSceneSavedFileMenuSubmenuIndexDelete, +} SPIMemSceneSavedFileMenuSubmenuIndex; + +static void spi_mem_scene_saved_file_menu_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_saved_file_menu_on_enter(void* context) { + SPIMemApp* app = context; + submenu_add_item( + app->submenu, + "Write", + SPIMemSceneSavedFileMenuSubmenuIndexWrite, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Compare", + SPIMemSceneSavedFileMenuSubmenuIndexCompare, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Info", + SPIMemSceneSavedFileMenuSubmenuIndexInfo, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Delete", + SPIMemSceneSavedFileMenuSubmenuIndexDelete, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_saved_file_menu_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, event.event); + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexWrite) { + app->mode = SPIMemModeWrite; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexCompare) { + app->mode = SPIMemModeCompare; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexDelete) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneDeleteConfirm); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexInfo) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneFileInfo); + success = true; + } + } + return success; +} + +void spi_mem_scene_saved_file_menu_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c new file mode 100644 index 000000000..cb48035b5 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c @@ -0,0 +1,22 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_select_file_on_enter(void* context) { + SPIMemApp* app = context; + if(spi_mem_file_select(app)) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, 0); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSavedFileMenu); + } else { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); + } +} + +bool spi_mem_scene_select_file_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void spi_mem_scene_select_file_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c new file mode 100644 index 000000000..c39c4a182 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c @@ -0,0 +1,45 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_select_model_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + spi_mem_chip_copy_chip_info(app->chip_info, *found_chips_get(app->found_chips, index)); + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_select_model_on_enter(void* context) { + SPIMemApp* app = context; + size_t models_on_vendor = 0; + for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { + if(spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index)) != + app->chip_vendor_enum) + continue; + submenu_add_item( + app->submenu, + spi_mem_chip_get_model_name(*found_chips_get(app->found_chips, index)), + index, + spi_mem_scene_select_model_submenu_callback, + app); + models_on_vendor++; + } + if(models_on_vendor == 1) spi_mem_scene_select_model_submenu_callback(context, 0); + submenu_set_header(app->submenu, "Choose chip model"); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_select_model_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetected); + success = true; + } + return success; +} + +void spi_mem_scene_select_model_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c new file mode 100644 index 000000000..c7f736f88 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c @@ -0,0 +1,70 @@ +#include "../spi_mem_app_i.h" +#include +#include + +ARRAY_DEF(vendors, uint32_t) +ALGO_DEF(vendors, ARRAY_OPLIST(vendors)) + +static void spi_mem_scene_select_vendor_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + app->chip_vendor_enum = index; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void spi_mem_scene_select_vendor_sort_vendors(SPIMemApp* app, vendors_t vendors_arr) { + for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { + vendors_push_back( + vendors_arr, spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index))); + } + vendors_uniq(vendors_arr); +} + +void spi_mem_scene_select_vendor_on_enter(void* context) { + SPIMemApp* app = context; + vendors_t vendors_arr; + vendors_init(vendors_arr); + spi_mem_scene_select_vendor_sort_vendors(app, vendors_arr); + size_t vendors_arr_size = vendors_size(vendors_arr); + if(vendors_arr_size == 1) + spi_mem_scene_select_vendor_submenu_callback(context, *vendors_get(vendors_arr, 0)); + for(size_t index = 0; index < vendors_arr_size; index++) { + uint32_t vendor_enum = *vendors_get(vendors_arr, index); + submenu_add_item( + app->submenu, + spi_mem_chip_get_vendor_name_by_enum(vendor_enum), + vendor_enum, + spi_mem_scene_select_vendor_submenu_callback, + app); + } + vendors_clear(vendors_arr); + submenu_set_header(app->submenu, "Choose chip vendor"); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +static void spi_mem_scene_select_vendor_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) + scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_select_vendor_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_select_vendor_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectModel); + success = true; + } + return success; +} + +void spi_mem_scene_select_vendor_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c new file mode 100644 index 000000000..b664df687 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c @@ -0,0 +1,84 @@ +#include "../spi_mem_app_i.h" + +typedef enum { + SPIMemSceneStartSubmenuIndexRead, + SPIMemSceneStartSubmenuIndexSaved, + SPIMemSceneStartSubmenuIndexErase, + SPIMemSceneStartSubmenuIndexWiring, + SPIMemSceneStartSubmenuIndexAbout +} SPIMemSceneStartSubmenuIndex; + +static void spi_mem_scene_start_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_start_on_enter(void* context) { + SPIMemApp* app = context; + submenu_add_item( + app->submenu, + "Read", + SPIMemSceneStartSubmenuIndexRead, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Saved", + SPIMemSceneStartSubmenuIndexSaved, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Erase", + SPIMemSceneStartSubmenuIndexErase, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Wiring", + SPIMemSceneStartSubmenuIndexWiring, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "About", + SPIMemSceneStartSubmenuIndexAbout, + spi_mem_scene_start_submenu_callback, + app); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneStart)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneStart, event.event); + if(event.event == SPIMemSceneStartSubmenuIndexRead) { + app->mode = SPIMemModeRead; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexSaved) { + furi_string_set(app->file_path, SPI_MEM_FILE_FOLDER); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexErase) { + app->mode = SPIMemModeErase; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexWiring) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneWiring); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexAbout) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneAbout); + success = true; + } + } + return success; +} + +void spi_mem_scene_start_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c new file mode 100644 index 000000000..d5e289e24 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c @@ -0,0 +1,56 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_storage_error_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_storage_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_storage_error_widget_callback, app); + widget_add_string_element( + app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "Storage error"); + widget_add_string_multiline_element( + app->widget, + 85, + 52, + AlignCenter, + AlignBottom, + FontSecondary, + "Error while\nworking with\nfilesystem"); + widget_add_icon_element(app->widget, 5, 6, &I_SDQuestion_35x43); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +static void spi_mem_scene_storage_error_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneChipDetect; + if(app->mode == SPIMemModeRead) scene = SPIMemSceneStart; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + if(app->mode == SPIMemModeDelete) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_storage_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_storage_error_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + spi_mem_scene_storage_error_set_previous_scene(app); + } + } + return success; +} +void spi_mem_scene_storage_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c new file mode 100644 index 000000000..39039466f --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c @@ -0,0 +1,40 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_success_popup_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventPopupBack); +} + +void spi_mem_scene_success_on_enter(void* context) { + SPIMemApp* app = context; + popup_set_icon(app->popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(app->popup, "Success!", 5, 7, AlignLeft, AlignTop); + popup_set_callback(app->popup, spi_mem_scene_success_popup_callback); + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 1500); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewPopup); +} + +static void spi_mem_scene_success_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneSelectFile; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_another_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_success_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventPopupBack) { + spi_mem_scene_success_set_previous_scene(app); + } + } + return success; +} + +void spi_mem_scene_success_on_exit(void* context) { + SPIMemApp* app = context; + popup_reset(app->popup); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c new file mode 100644 index 000000000..08a8d1057 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c @@ -0,0 +1,59 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_verify_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewVerifySkip); +} + +static void spi_mem_scene_verify_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_verify_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_verify_callback( + app->view_progress, spi_mem_scene_verify_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_cyan); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_verify_start(app->chip_info, app->worker, spi_mem_scene_verify_callback, app); +} + +bool spi_mem_scene_verify_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewVerifySkip) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else if(event.event == SPIMemCustomEventWorkerVerifyFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerifyError); + } + } + return success; +} +void spi_mem_scene_verify_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c new file mode 100644 index 000000000..fbe954fa6 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c @@ -0,0 +1,43 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_verify_error_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_verify_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_verify_error_widget_callback, app); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Verification error"); + widget_add_string_element( + app->widget, 64, 21, AlignCenter, AlignBottom, FontSecondary, "Data mismatch"); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_verify_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } + } + return success; +} +void spi_mem_scene_verify_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c new file mode 100644 index 000000000..22036f4bc --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c @@ -0,0 +1,18 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +void spi_mem_scene_wiring_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_Wiring_SPI_128x64); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_wiring_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void spi_mem_scene_wiring_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c new file mode 100644 index 000000000..dfa384fbb --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c @@ -0,0 +1,58 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_write_progress_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); +} + +static void spi_mem_scene_write_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_write_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_write_callback( + app->view_progress, spi_mem_scene_write_progress_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_cyan); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_write_start(app->chip_info, app->worker, spi_mem_scene_write_callback, app); +} + +bool spi_mem_scene_write_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewReadCancel) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } + return success; +} +void spi_mem_scene_write_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/plugins/spi_mem_manager/spi_mem_app.c new file mode 100644 index 000000000..63531b74c --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app.c @@ -0,0 +1,112 @@ +#include +#include "spi_mem_app_i.h" +#include "spi_mem_files.h" +#include "lib/spi/spi_mem_chip_i.h" + +static bool spi_mem_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SPIMemApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool spi_mem_back_event_callback(void* context) { + furi_assert(context); + SPIMemApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +SPIMemApp* spi_mem_alloc(void) { + SPIMemApp* instance = malloc(sizeof(SPIMemApp)); + + instance->file_path = furi_string_alloc(); + instance->gui = furi_record_open(RECORD_GUI); + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + instance->view_dispatcher = view_dispatcher_alloc(); + instance->scene_manager = scene_manager_alloc(&spi_mem_scene_handlers, instance); + instance->submenu = submenu_alloc(); + instance->dialog_ex = dialog_ex_alloc(); + instance->popup = popup_alloc(); + instance->worker = spi_mem_worker_alloc(); + instance->dialogs = furi_record_open(RECORD_DIALOGS); + instance->storage = furi_record_open(RECORD_STORAGE); + instance->widget = widget_alloc(); + instance->chip_info = malloc(sizeof(SPIMemChip)); + found_chips_init(instance->found_chips); + instance->view_progress = spi_mem_view_progress_alloc(); + instance->view_detect = spi_mem_view_detect_alloc(); + instance->text_input = text_input_alloc(); + instance->mode = SPIMemModeUnknown; + + furi_string_set(instance->file_path, SPI_MEM_FILE_FOLDER); + + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, spi_mem_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, spi_mem_back_event_callback); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewSubmenu, submenu_get_view(instance->submenu)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewPopup, popup_get_view(instance->popup)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewWidget, widget_get_view(instance->widget)); + view_dispatcher_add_view( + instance->view_dispatcher, + SPIMemViewProgress, + spi_mem_view_progress_get_view(instance->view_progress)); + view_dispatcher_add_view( + instance->view_dispatcher, + SPIMemViewDetect, + spi_mem_view_detect_get_view(instance->view_detect)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewTextInput, text_input_get_view(instance->text_input)); + + furi_hal_power_enable_otg(); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); + scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart); + return instance; +} + +void spi_mem_free(SPIMemApp* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDialogEx); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewPopup); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewWidget); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewProgress); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDetect); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewTextInput); + spi_mem_view_progress_free(instance->view_progress); + spi_mem_view_detect_free(instance->view_detect); + submenu_free(instance->submenu); + dialog_ex_free(instance->dialog_ex); + popup_free(instance->popup); + widget_free(instance->widget); + text_input_free(instance->text_input); + view_dispatcher_free(instance->view_dispatcher); + scene_manager_free(instance->scene_manager); + spi_mem_worker_free(instance->worker); + free(instance->chip_info); + found_chips_clear(instance->found_chips); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + furi_string_free(instance->file_path); + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); + furi_hal_power_disable_otg(); + free(instance); +} + +int32_t spi_mem_app(void* p) { + UNUSED(p); + SPIMemApp* instance = spi_mem_alloc(); + spi_mem_file_create_folder(instance); + view_dispatcher_run(instance->view_dispatcher); + spi_mem_free(instance); + return 0; +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.h b/applications/plugins/spi_mem_manager/spi_mem_app.h new file mode 100644 index 000000000..37ac927db --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct SPIMemApp SPIMemApp; diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/plugins/spi_mem_manager/spi_mem_app_i.h new file mode 100644 index 000000000..4ce056175 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app_i.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include "spi_mem_app.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/spi_mem_scene.h" +#include "lib/spi/spi_mem_worker.h" +#include "spi_mem_manager_icons.h" +#include "views/spi_mem_view_progress.h" +#include "views/spi_mem_view_detect.h" + +#define TAG "SPIMem" +#define SPI_MEM_FILE_EXTENSION ".bin" +#define SPI_MEM_FILE_FOLDER EXT_PATH("spimem") +#define SPI_MEM_FILE_NAME_SIZE 100 +#define SPI_MEM_TEXT_BUFFER_SIZE 128 + +typedef enum { + SPIMemModeRead, + SPIMemModeWrite, + SPIMemModeCompare, + SPIMemModeErase, + SPIMemModeDelete, + SPIMemModeUnknown +} SPIMemMode; + +struct SPIMemApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + NotificationApp* notifications; + FuriString* file_path; + DialogsApp* dialogs; + Storage* storage; + File* file; + Widget* widget; + SPIMemWorker* worker; + SPIMemChip* chip_info; + found_chips_t found_chips; + uint32_t chip_vendor_enum; + SPIMemProgressView* view_progress; + SPIMemDetectView* view_detect; + TextInput* text_input; + SPIMemMode mode; + char text_buffer[SPI_MEM_TEXT_BUFFER_SIZE + 1]; +}; + +typedef enum { + SPIMemViewSubmenu, + SPIMemViewDialogEx, + SPIMemViewPopup, + SPIMemViewWidget, + SPIMemViewTextInput, + SPIMemViewProgress, + SPIMemViewDetect +} SPIMemView; + +typedef enum { + SPIMemCustomEventViewReadCancel, + SPIMemCustomEventViewVerifySkip, + SPIMemCustomEventTextEditResult, + SPIMemCustomEventPopupBack +} SPIMemCustomEvent; diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/plugins/spi_mem_manager/spi_mem_files.c new file mode 100644 index 000000000..a7374da19 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_files.c @@ -0,0 +1,74 @@ +#include "spi_mem_app_i.h" + +void spi_mem_file_create_folder(SPIMemApp* app) { + if(!storage_simply_mkdir(app->storage, SPI_MEM_FILE_FOLDER)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); + } +} + +bool spi_mem_file_delete(SPIMemApp* app) { + return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path))); +} + +bool spi_mem_file_select(SPIMemApp* app) { + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px); + browser_options.base_path = SPI_MEM_FILE_FOLDER; + bool success = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + return success; +} + +bool spi_mem_file_create_open(SPIMemApp* app) { + bool success = false; + app->file = storage_file_alloc(app->storage); + do { + if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { + if(!spi_mem_file_delete(app)) break; + size_t filename_start = furi_string_search_rchar(app->file_path, '/'); + furi_string_left(app->file_path, filename_start); + } + furi_string_cat_printf(app->file_path, "/%s%s", app->text_buffer, SPI_MEM_FILE_EXTENSION); + if(!storage_file_open( + app->file, furi_string_get_cstr(app->file_path), FSAM_WRITE, FSOM_CREATE_NEW)) + break; + success = true; + } while(0); + if(!success) { //-V547 + dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); + } + return success; +} + +bool spi_mem_file_open(SPIMemApp* app) { + app->file = storage_file_alloc(app->storage); + if(!storage_file_open( + app->file, furi_string_get_cstr(app->file_path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); + return false; + } + return true; +} + +bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size) { + if(storage_file_write(app->file, data, size) != size) return false; + return true; +} + +bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size) { + if(storage_file_read(app->file, data, size) != size) return false; + return true; +} + +void spi_mem_file_close(SPIMemApp* app) { + storage_file_close(app->file); + storage_file_free(app->file); +} + +size_t spi_mem_file_get_size(SPIMemApp* app) { + FileInfo file_info; + if(storage_common_stat(app->storage, furi_string_get_cstr(app->file_path), &file_info) != + FSE_OK) + return 0; + return file_info.size; +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/plugins/spi_mem_manager/spi_mem_files.h new file mode 100644 index 000000000..0e735d951 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_files.h @@ -0,0 +1,14 @@ +#pragma once +#include "spi_mem_app.h" + +void spi_mem_file_create_folder(SPIMemApp* app); +bool spi_mem_file_select(SPIMemApp* app); +bool spi_mem_file_create(SPIMemApp* app, const char* file_name); +bool spi_mem_file_delete(SPIMemApp* app); +bool spi_mem_file_create_open(SPIMemApp* app); +bool spi_mem_file_open(SPIMemApp* app); +bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size); +bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size); +void spi_mem_file_close(SPIMemApp* app); +void spi_mem_file_show_storage_error(SPIMemApp* app, const char* error_text); +size_t spi_mem_file_get_size(SPIMemApp* app); diff --git a/applications/plugins/spi_mem_manager/tools/README.md b/applications/plugins/spi_mem_manager/tools/README.md new file mode 100644 index 000000000..91080941f --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/README.md @@ -0,0 +1,7 @@ +This utility can convert nofeletru's UsbAsp-flash's chiplist.xml to C array + +Usage: +```bash + ./chiplist_convert.py chiplist/chiplist.xml + mv spi_mem_chip_arr.c ../lib/spi/spi_mem_chip_arr.c +``` diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE b/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE new file mode 100644 index 000000000..56364f150 --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 nofeletru + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml b/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml new file mode 100644 index 000000000..91a654743 --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml @@ -0,0 +1,984 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_25AA010A page="16" size="128" spicmd="95"/> + <_25AA020A page="16" size="256" spicmd="95"/> + <_25AA040 page="16" size="512" spicmd="95"/> + <_25AA040A page="16" size="512" spicmd="95"/> + <_25AA080 page="16" size="1024" spicmd="95"/> + <_25AA080A page="16" size="1024" spicmd="95"/> + <_25AA080B page="32" size="1024" spicmd="95"/> + <_25AA080C page="16" size="1024" spicmd="95"/> + <_25AA080D page="32" size="1024" spicmd="95"/> + <_25AA1024 page="256" size="131072" spicmd="95"/> + <_25AA128 page="64" size="16384" spicmd="95"/> + <_25AA160 page="16" size="2048" spicmd="95"/> + <_25AA160A page="16" size="2048" spicmd="95"/> + <_25AA160B page="32" size="2048" spicmd="95"/> + <_25AA256 page="64" size="32768" spicmd="95"/> + <_25AA320 page="32" size="4096" spicmd="95"/> + <_25AA512 page="128" size="65536" spicmd="95"/> + <_25AA640 page="32" size="8192" spicmd="95"/> + <_25C040 page="16" size="512" spicmd="95"/> + <_25C080 page="16" size="1024" spicmd="95"/> + <_25C160 page="16" size="2048" spicmd="95"/> + <_25C320 page="32" size="4096" spicmd="95"/> + <_25C640 page="32" size="8192" spicmd="95"/> + <_25LC010A page="16" size="128" spicmd="95"/> + <_25LC020A page="16" size="256" spicmd="95"/> + <_25LC040 page="16" size="512" spicmd="95"/> + <_25LC040A page="16" size="512" spicmd="95"/> + <_25LC080 page="16" size="1024" spicmd="95"/> + <_25LC080A page="16" size="1024" spicmd="95"/> + <_25LC080B page="32" size="1024" spicmd="95"/> + <_25LC080C page="16" size="1024" spicmd="95"/> + <_25LC080D page="32" size="1024" spicmd="95"/> + <_25LC1024 page="256" size="131072" spicmd="95"/> + <_25LC128 page="64" size="16384" spicmd="95"/> + <_25LC160 page="16" size="2048" spicmd="95"/> + <_25LC160A page="16" size="2048" spicmd="95"/> + <_25LC160B page="32" size="2048" spicmd="95"/> + <_25LC256 page="64" size="32768" spicmd="95"/> + <_25LC320 page="32" size="4096" spicmd="95"/> + <_25LC512 page="128" size="65536" spicmd="95"/> + <_25LC640 page="32" size="8192" spicmd="95"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_24Cxxx> + + <_24C01 page="1" size="128" addrtype="1"/> + <_24C02 page="1" size="256" addrtype="1"/> + <_24C04 page="1" size="512" addrtype="2"/> + <_24C08 page="16" size="1024" addrtype="3"/> + <_24C16 page="16" size="2048" addrtype="4"/> + <_24C32 page="32" size="4096" addrtype="5"/> + <_24C64 page="32" size="8192" addrtype="5"/> + <_24C128 page="64" size="16384" addrtype="5"/> + <_24C256 page="64" size="32768" addrtype="5"/> + <_24C512 page="128" size="65536" addrtype="5"/> + <_24C1024 page="128" size="131072" addrtype="6"/> + + + + + + + + + + + + + diff --git a/applications/plugins/spi_mem_manager/tools/chiplist_convert.py b/applications/plugins/spi_mem_manager/tools/chiplist_convert.py new file mode 100755 index 000000000..8b623eb3e --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist_convert.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 + +import argparse +import xml.etree.ElementTree as XML +import sys + + +def getArgs(): + parser = argparse.ArgumentParser( + description="chiplist.xml to C array converter", + ) + parser.add_argument("file", help="chiplist.xml file") + return parser.parse_args() + + +def getXML(file): + tree = XML.parse(file) + root = tree.getroot() + return root + + +def parseChip(cur, arr, vendor, vendorCodeArr): + chip = {} + chipAttr = cur.attrib + if "page" not in chipAttr: # chip without page size not supported + return + if "id" not in chipAttr: # I2C not supported yet + return + if len(chipAttr["id"]) < 6: # ID wihout capacity id not supported yet + return + chip["modelName"] = cur.tag + chip["vendorEnum"] = "SPIMemChipVendor" + vendor + chip["vendorID"] = "0x" + chipAttr["id"][0] + chipAttr["id"][1] + chip["typeID"] = chipAttr["id"][2] + chipAttr["id"][3] + chip["capacityID"] = chipAttr["id"][4] + chipAttr["id"][5] + chip["size"] = chipAttr["size"] + if chipAttr["page"] == "SSTW": + chip["writeMode"] = "SPIMemChipWriteModeAAIWord" + chip["pageSize"] = "1" + elif chipAttr["page"] == "SSTB": + chip["writeMode"] = "SPIMemChipWriteModeAAIByte" + chip["pageSize"] = "1" + else: + chip["writeMode"] = "SPIMemChipWriteModePage" + chip["pageSize"] = chipAttr["page"] + arr.append(chip) + vendorCodeArr[vendor].add(chip["vendorID"]) + + +def cleanEmptyVendors(vendors): + for cur in list(vendors): + if not vendors[cur]: + vendors.pop(cur) + + +def getVendors(xml, interface): + arr = {} + for cur in xml.find(interface): + arr[cur.tag] = set() + return arr + + +def parseXML(xml, interface, vendorCodeArr): + arr = [] + for vendor in xml.find(interface): + for cur in vendor: + parseChip(cur, arr, vendor.tag, vendorCodeArr) + return arr + + +def getVendorNameEnum(vendorID): + try: + return vendors[vendorID] + except: + print("Unknown vendor: " + vendorID) + sys.exit(1) + + +def generateCArr(arr, filename): + with open(filename, "w") as out: + print('#include "spi_mem_chip_i.h"', file=out) + print("const SPIMemChip SPIMemChips[] = {", file=out) + for cur in arr: + print(" {" + cur["vendorID"] + ",", file=out, end="") + print(" 0x" + cur["typeID"] + ",", file=out, end="") + print(" 0x" + cur["capacityID"] + ",", file=out, end="") + print(' "' + cur["modelName"] + '",', file=out, end="") + print(" " + cur["size"] + ",", file=out, end="") + print(" " + cur["pageSize"] + ",", file=out, end="") + print(" " + cur["vendorEnum"] + ",", file=out, end="") + if cur == arr[-1]: + print(" " + cur["writeMode"] + "}};", file=out) + else: + print(" " + cur["writeMode"] + "},", file=out) + +def main(): + filename = "spi_mem_chip_arr.c" + args = getArgs() + xml = getXML(args.file) + vendors = getVendors(xml, "SPI") + chipArr = parseXML(xml, "SPI", vendors) + cleanEmptyVendors(vendors) + for cur in vendors: + print(' {"' + cur + '", SPIMemChipVendor' + cur + "},") + generateCArr(chipArr, filename) + + +if __name__ == "__main__": + main() diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c new file mode 100644 index 000000000..eddf36e49 --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c @@ -0,0 +1,64 @@ +#include "spi_mem_view_detect.h" +#include "spi_mem_manager_icons.h" +#include + +struct SPIMemDetectView { + View* view; + IconAnimation* icon; + SPIMemDetectViewCallback callback; + void* cb_ctx; +}; + +typedef struct { + IconAnimation* icon; +} SPIMemDetectViewModel; + +View* spi_mem_view_detect_get_view(SPIMemDetectView* app) { + return app->view; +} + +static void spi_mem_view_detect_draw_callback(Canvas* canvas, void* context) { + SPIMemDetectViewModel* model = context; + canvas_set_font(canvas, FontPrimary); + canvas_draw_icon_animation(canvas, 0, 0, model->icon); + canvas_draw_str_aligned(canvas, 64, 26, AlignLeft, AlignCenter, "Detecting"); + canvas_draw_str_aligned(canvas, 64, 36, AlignLeft, AlignCenter, "SPI chip..."); +} + +static void spi_mem_view_detect_enter_callback(void* context) { + SPIMemDetectView* app = context; + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_start(model->icon); }, false); +} + +static void spi_mem_view_detect_exit_callback(void* context) { + SPIMemDetectView* app = context; + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_stop(model->icon); }, false); +} + +SPIMemDetectView* spi_mem_view_detect_alloc() { + SPIMemDetectView* app = malloc(sizeof(SPIMemDetectView)); + app->view = view_alloc(); + view_set_context(app->view, app); + view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemDetectViewModel)); + with_view_model( + app->view, + SPIMemDetectViewModel * model, + { + model->icon = icon_animation_alloc(&A_ChipLooking_64x64); + view_tie_icon_animation(app->view, model->icon); + }, + false); + view_set_draw_callback(app->view, spi_mem_view_detect_draw_callback); + view_set_enter_callback(app->view, spi_mem_view_detect_enter_callback); + view_set_exit_callback(app->view, spi_mem_view_detect_exit_callback); + return app; +} + +void spi_mem_view_detect_free(SPIMemDetectView* app) { + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_free(model->icon); }, false); + view_free(app->view); + free(app); +} diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h new file mode 100644 index 000000000..f95edb60d --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h @@ -0,0 +1,9 @@ +#pragma once +#include + +typedef struct SPIMemDetectView SPIMemDetectView; +typedef void (*SPIMemDetectViewCallback)(void* context); + +View* spi_mem_view_detect_get_view(SPIMemDetectView* app); +SPIMemDetectView* spi_mem_view_detect_alloc(); +void spi_mem_view_detect_free(SPIMemDetectView* app); diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c new file mode 100644 index 000000000..790f97997 --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c @@ -0,0 +1,230 @@ +#include "spi_mem_view_progress.h" +#include + +struct SPIMemProgressView { + View* view; + SPIMemProgressViewCallback callback; + void* cb_ctx; +}; + +typedef enum { + SPIMemProgressViewTypeRead, + SPIMemProgressViewTypeVerify, + SPIMemProgressViewTypeWrite, + SPIMemProgressViewTypeUnknown +} SPIMemProgressViewType; + +typedef struct { + size_t chip_size; + size_t file_size; + size_t blocks_written; + size_t block_size; + float progress; + SPIMemProgressViewType view_type; +} SPIMemProgressViewModel; + +View* spi_mem_view_progress_get_view(SPIMemProgressView* app) { + return app->view; +} + +static void spi_mem_view_progress_draw_progress(Canvas* canvas, float progress) { + FuriString* progress_str = furi_string_alloc(); + if(progress > 1.0) progress = 1.0; + furi_string_printf(progress_str, "%d %%", (int)(progress * 100)); + elements_progress_bar(canvas, 13, 35, 100, progress); + canvas_draw_str_aligned( + canvas, 64, 25, AlignCenter, AlignTop, furi_string_get_cstr(progress_str)); + furi_string_free(progress_str); +} + +static void + spi_mem_view_progress_read_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Reading dump"); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_left(canvas, "Cancel"); +} + +static void + spi_mem_view_progress_draw_size_warning(Canvas* canvas, SPIMemProgressViewModel* model) { + if(model->file_size > model->chip_size) { + canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to chip!"); + } + if(model->chip_size > model->file_size) { + canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to file!"); + } +} + +static void + spi_mem_view_progress_verify_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Verifying dump"); + spi_mem_view_progress_draw_size_warning(canvas, model); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_center(canvas, "Skip"); +} + +static void + spi_mem_view_progress_write_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Writing dump"); + spi_mem_view_progress_draw_size_warning(canvas, model); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_left(canvas, "Cancel"); +} + +static void spi_mem_view_progress_draw_callback(Canvas* canvas, void* context) { + SPIMemProgressViewModel* model = context; + SPIMemProgressViewType view_type = model->view_type; + if(view_type == SPIMemProgressViewTypeRead) { + spi_mem_view_progress_read_draw_callback(canvas, model); + } else if(view_type == SPIMemProgressViewTypeVerify) { + spi_mem_view_progress_verify_draw_callback(canvas, model); + } else if(view_type == SPIMemProgressViewTypeWrite) { + spi_mem_view_progress_write_draw_callback(canvas, model); + } +} + +static bool + spi_mem_view_progress_read_write_input_callback(InputEvent* event, SPIMemProgressView* app) { + bool success = false; + if(event->type == InputTypeShort && event->key == InputKeyLeft) { + if(app->callback) { + app->callback(app->cb_ctx); + } + success = true; + } + return success; +} + +static bool + spi_mem_view_progress_verify_input_callback(InputEvent* event, SPIMemProgressView* app) { + bool success = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(app->callback) { + app->callback(app->cb_ctx); + } + success = true; + } + return success; +} + +static bool spi_mem_view_progress_input_callback(InputEvent* event, void* context) { + SPIMemProgressView* app = context; + bool success = false; + SPIMemProgressViewType view_type; + with_view_model( + app->view, SPIMemProgressViewModel * model, { view_type = model->view_type; }, true); + if(view_type == SPIMemProgressViewTypeRead) { + success = spi_mem_view_progress_read_write_input_callback(event, app); + } else if(view_type == SPIMemProgressViewTypeVerify) { + success = spi_mem_view_progress_verify_input_callback(event, app); + } else if(view_type == SPIMemProgressViewTypeWrite) { + success = spi_mem_view_progress_read_write_input_callback(event, app); + } + return success; +} + +SPIMemProgressView* spi_mem_view_progress_alloc() { + SPIMemProgressView* app = malloc(sizeof(SPIMemProgressView)); + app->view = view_alloc(); + view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemProgressViewModel)); + view_set_context(app->view, app); + view_set_draw_callback(app->view, spi_mem_view_progress_draw_callback); + view_set_input_callback(app->view, spi_mem_view_progress_input_callback); + spi_mem_view_progress_reset(app); + return app; +} + +void spi_mem_view_progress_free(SPIMemProgressView* app) { + view_free(app->view); + free(app); +} + +void spi_mem_view_progress_set_read_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeRead; }, + true); +} + +void spi_mem_view_progress_set_verify_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeVerify; }, + true); +} + +void spi_mem_view_progress_set_write_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeWrite; }, + true); +} + +void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->chip_size = chip_size; }, true); +} + +void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->file_size = file_size; }, true); +} + +void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->block_size = block_size; }, true); +} + +static size_t spi_mem_view_progress_set_total_size(SPIMemProgressViewModel* model) { + size_t total_size = model->chip_size; + if((model->chip_size > model->file_size) && model->view_type != SPIMemProgressViewTypeRead) { + total_size = model->file_size; + } + return total_size; +} + +void spi_mem_view_progress_inc_progress(SPIMemProgressView* app) { + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { + size_t total_size = spi_mem_view_progress_set_total_size(model); + if(total_size == 0) total_size = 1; + model->blocks_written++; + model->progress = + ((float)model->block_size * (float)model->blocks_written) / ((float)total_size); + }, + true); +} + +void spi_mem_view_progress_reset(SPIMemProgressView* app) { + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { + model->blocks_written = 0; + model->block_size = 0; + model->chip_size = 0; + model->file_size = 0; + model->progress = 0; + model->view_type = SPIMemProgressViewTypeUnknown; + }, + true); +} diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h new file mode 100644 index 000000000..6a8645b6c --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h @@ -0,0 +1,26 @@ +#pragma once +#include + +typedef struct SPIMemProgressView SPIMemProgressView; +typedef void (*SPIMemProgressViewCallback)(void* context); + +View* spi_mem_view_progress_get_view(SPIMemProgressView* app); +SPIMemProgressView* spi_mem_view_progress_alloc(); +void spi_mem_view_progress_free(SPIMemProgressView* app); +void spi_mem_view_progress_set_read_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_verify_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_write_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size); +void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size); +void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size); +void spi_mem_view_progress_inc_progress(SPIMemProgressView* app); +void spi_mem_view_progress_reset(SPIMemProgressView* app); From 1be72a94a82125101aaf6cf81790825d6e85853b Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 14:06:52 +0000 Subject: [PATCH 05/75] Fix build for now --- .github/workflows/build.yml | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b19d123b..edaff1d31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,14 +79,14 @@ jobs: - name: 'Bundle core2 firmware' run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - + - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: name: updater path: | artifacts/f7-* - + - name: 'Firmware artifact' uses: actions/upload-artifact@v3 with: @@ -94,31 +94,31 @@ jobs: path: | artifacts - - name: 'Find Previous Comment' - if: ${{ github.event.pull_request }} - uses: peter-evans/find-comment@v1 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'Compiled firmware for commit' - - - name: Artifact info - id: artifact-info - uses: dawidd6/action-download-artifact@v2 - with: - dry_run: true + # - name: 'Find Previous Comment' + # if: ${{ github.event.pull_request }} + # uses: peter-evans/find-comment@v1 + # id: fc + # with: + # issue-number: ${{ github.event.pull_request.number }} + # comment-author: 'github-actions[bot]' + # body-includes: 'Compiled firmware for commit' - - name: 'Create or update comment' - if: ${{ github.event.pull_request}} - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** - - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) - edit-mode: replace + # - name: Artifact info + # id: artifact-info + # uses: dawidd6/action-download-artifact@v2 + # with: + # dry_run: true + + # - name: 'Create or update comment' + # if: ${{ github.event.pull_request}} + # uses: peter-evans/create-or-update-comment@v1 + # with: + # comment-id: ${{ steps.fc.outputs.comment-id }} + # issue-number: ${{ github.event.pull_request.number }} + # body: | + # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) + # edit-mode: replace compact: if: ${{ !startsWith(github.ref, 'refs/tags') }} From 3acfe5b2153914c35f74c8d906059d26e426817c Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 14:16:03 +0000 Subject: [PATCH 06/75] Format --- applications/services/bt/bt_service/bt.c | 4 ++-- applications/services/bt/bt_service/bt.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index cc5e8f33e..6949bbc41 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -445,12 +445,12 @@ GapPairing bt_get_profile_pairing_method(Bt* bt) { return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); } -void bt_disable_peer_key_update(Bt *bt) { +void bt_disable_peer_key_update(Bt* bt) { UNUSED(bt); furi_hal_bt_set_key_storage_change_callback(NULL, NULL); } -void bt_enable_peer_key_update(Bt *bt) { +void bt_enable_peer_key_update(Bt* bt) { furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); } diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index ddf9382e8..a79c227f7 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -55,13 +55,13 @@ GapPairing bt_get_profile_pairing_method(Bt* bt); /** Stop saving new peer key to flash (in .bt.keys file) * */ -void bt_disable_peer_key_update(Bt *bt); +void bt_disable_peer_key_update(Bt* bt); /** Enable saving peer key to internal flash (enable by default) * * @note This function should be called if bt_disable_peer_key_update was called before */ -void bt_enable_peer_key_update(Bt *bt); +void bt_enable_peer_key_update(Bt* bt); /** Disconnect from Central * From 6e179bda1f69f3ce1c216faa90d24577ebeea631 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Mon, 6 Feb 2023 17:56:36 +0300 Subject: [PATCH 07/75] Script that can find programmer and flash firmware via it. (#2193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Init * Fallback to networked interface * remove unneeded cmsis_dap_backend * serial number * windows :( * remove jlink, fix path handling * scripts: program: path normalization * scripts: program: path normalization: second encounter Co-authored-by: hedger Co-authored-by: あく --- scripts/program.py | 459 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100755 scripts/program.py diff --git a/scripts/program.py b/scripts/program.py new file mode 100755 index 000000000..c140a9024 --- /dev/null +++ b/scripts/program.py @@ -0,0 +1,459 @@ +#!/usr/bin/env python3 +import typing +import subprocess +import logging +import time +import os +import socket + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from flipper.app import App + + +class Programmer(ABC): + @abstractmethod + def flash(self, bin: str) -> bool: + pass + + @abstractmethod + def probe(self) -> bool: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + @abstractmethod + def set_serial(self, serial: str): + pass + + +@dataclass +class OpenOCDInterface: + name: str + file: str + serial_cmd: str + additional_args: typing.Optional[list[str]] = None + + +class OpenOCDProgrammer(Programmer): + def __init__(self, interface: OpenOCDInterface): + self.interface = interface + self.logger = logging.getLogger("OpenOCD") + self.serial: typing.Optional[str] = None + + def _add_file(self, params: list[str], file: str): + params.append("-f") + params.append(file) + + def _add_command(self, params: list[str], command: str): + params.append("-c") + params.append(command) + + def _add_serial(self, params: list[str], serial: str): + self._add_command(params, f"{self.interface.serial_cmd} {serial}") + + def set_serial(self, serial: str): + self.serial = serial + + def flash(self, bin: str) -> bool: + i = self.interface + + if os.altsep: + bin = bin.replace(os.sep, os.altsep) + + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, i.file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if i.additional_args: + for a in i.additional_args: + self._add_command(openocd_launch_params, a) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + self._add_command(openocd_launch_params, f"program {bin} reset exit 0x8000000") + + # join the list of parameters into a string, but add quote if there are spaces + openocd_launch_params_string = " ".join( + [f'"{p}"' if " " in p else p for p in openocd_launch_params] + ) + + self.logger.debug(f"Launching: {openocd_launch_params_string}") + + process = subprocess.Popen( + openocd_launch_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + while process.poll() is None: + time.sleep(0.25) + print(".", end="", flush=True) + print() + + success = process.returncode == 0 + + if not success: + self.logger.error("OpenOCD failed to flash") + if process.stdout: + self.logger.error(process.stdout.read().decode("utf-8").strip()) + + return success + + def probe(self) -> bool: + i = self.interface + + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, i.file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if i.additional_args: + for a in i.additional_args: + self._add_command(openocd_launch_params, a) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + self._add_command(openocd_launch_params, "exit") + + self.logger.debug(f"Launching: {' '.join(openocd_launch_params)}") + + process = subprocess.Popen( + openocd_launch_params, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + ) + + # Wait for OpenOCD to end and get the return code + process.wait() + found = process.returncode == 0 + + if process.stdout: + self.logger.debug(process.stdout.read().decode("utf-8").strip()) + + return found + + def get_name(self) -> str: + return self.interface.name + + +def blackmagic_find_serial(serial: str): + import serial.tools.list_ports as list_ports + + if serial and os.name == "nt": + if not serial.startswith("\\\\.\\"): + serial = f"\\\\.\\{serial}" + + ports = list(list_ports.grep("blackmagic")) + if len(ports) == 0: + return None + elif len(ports) > 2: + if serial: + ports = list( + filter( + lambda p: p.serial_number == serial + or p.name == serial + or p.device == serial, + ports, + ) + ) + if len(ports) == 0: + return None + + if len(ports) > 2: + raise Exception("More than one Blackmagic probe found") + + # If you're getting any issues with auto lookup, uncomment this + # print("\n".join([f"{p.device} {vars(p)}" for p in ports])) + port = sorted(ports, key=lambda p: f"{p.location}_{p.name}")[0] + + if serial: + if ( + serial != port.serial_number + and serial != port.name + and serial != port.device + ): + return None + + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + +def _resolve_hostname(hostname): + try: + return socket.gethostbyname(hostname) + except socket.gaierror: + return None + + +def blackmagic_find_networked(serial: str): + if not serial: + serial = "blackmagic.local" + + # remove the tcp: prefix if it's there + if serial.startswith("tcp:"): + serial = serial[4:] + + # remove the port if it's there + if ":" in serial: + serial = serial.split(":")[0] + + if not (probe := _resolve_hostname(serial)): + return None + + return f"tcp:{probe}:2345" + + +class BlackmagicProgrammer(Programmer): + def __init__( + self, + port_resolver, # typing.Callable[typing.Union[str, None], typing.Optional[str]] + name: str, + ): + self.port_resolver = port_resolver + self.name = name + self.logger = logging.getLogger("BlackmagicUSB") + self.port: typing.Optional[str] = None + + def _add_command(self, params: list[str], command: str): + params.append("-ex") + params.append(command) + + def _valid_ip(self, address): + try: + socket.inet_aton(address) + return True + except: + return False + + def set_serial(self, serial: str): + if self._valid_ip(serial): + self.port = f"{serial}:2345" + elif ip := _resolve_hostname(serial): + self.port = f"{ip}:2345" + else: + self.port = serial + + def flash(self, bin: str) -> bool: + if not self.port: + if not self.probe(): + return False + + # We can convert .bin to .elf with objcopy: + # arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf + # But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor. + elf = bin.replace(".bin", ".elf") + if not os.path.exists(elf): + self.logger.error( + f"Sorry, but Blackmagic can't flash .bin file, and {elf} doesn't exist" + ) + return False + + # arm-none-eabi-gdb build/f7-firmware-D/firmware.bin + # -ex 'set pagination off' + # -ex 'target extended-remote /dev/cu.usbmodem21201' + # -ex 'set confirm off' + # -ex 'monitor swdp_scan' + # -ex 'attach 1' + # -ex 'set mem inaccessible-by-default off' + # -ex 'load' + # -ex 'compare-sections' + # -ex 'quit' + + gdb_launch_params = ["arm-none-eabi-gdb", elf] + self._add_command(gdb_launch_params, f"target extended-remote {self.port}") + self._add_command(gdb_launch_params, "set pagination off") + self._add_command(gdb_launch_params, "set confirm off") + self._add_command(gdb_launch_params, "monitor swdp_scan") + self._add_command(gdb_launch_params, "attach 1") + self._add_command(gdb_launch_params, "set mem inaccessible-by-default off") + self._add_command(gdb_launch_params, "load") + self._add_command(gdb_launch_params, "compare-sections") + self._add_command(gdb_launch_params, "quit") + + self.logger.debug(f"Launching: {' '.join(gdb_launch_params)}") + + process = subprocess.Popen( + gdb_launch_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + while process.poll() is None: + time.sleep(0.5) + print(".", end="", flush=True) + print() + + if not process.stdout: + return False + + output = process.stdout.read().decode("utf-8").strip() + flashed = "Loading section .text," in output + + # Check flash verification + if "MIS-MATCHED!" in output: + flashed = False + + if "target image does not match the loaded file" in output: + flashed = False + + if not flashed: + self.logger.error("Blackmagic failed to flash") + self.logger.error(output) + + return flashed + + def probe(self) -> bool: + if not (port := self.port_resolver(self.port)): + return False + + self.port = port + return True + + def get_name(self) -> str: + return self.name + + +programmers: list[Programmer] = [ + OpenOCDProgrammer( + OpenOCDInterface( + "cmsis-dap", + "interface/cmsis-dap.cfg", + "cmsis_dap_serial", + ["transport select swd"], + ), + ), + OpenOCDProgrammer( + OpenOCDInterface( + "stlink", "interface/stlink.cfg", "hla_serial", ["transport select hla_swd"] + ), + ), + BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"), +] + +network_programmers = [ + BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi") +] + + +class Main(App): + def init(self): + self.subparsers = self.parser.add_subparsers(help="sub-command help") + self.parser_flash = self.subparsers.add_parser("flash", help="Flash a binary") + self.parser_flash.add_argument( + "bin", + type=str, + help="Binary to flash", + ) + interfaces = [i.get_name() for i in programmers] + interfaces.extend([i.get_name() for i in network_programmers]) + self.parser_flash.add_argument( + "--interface", + choices=interfaces, + type=str, + help="Interface to use", + ) + self.parser_flash.add_argument( + "--serial", + type=str, + help="Serial number or port of the programmer", + ) + self.parser_flash.set_defaults(func=self.flash) + + def _search_interface(self, serial: typing.Optional[str]) -> list[Programmer]: + found_programmers = [] + + for p in programmers: + name = p.get_name() + if serial: + p.set_serial(serial) + self.logger.debug(f"Trying {name} with {serial}") + else: + self.logger.debug(f"Trying {name}") + + if p.probe(): + self.logger.debug(f"Found {name}") + found_programmers += [p] + else: + self.logger.debug(f"Failed to probe {name}") + + return found_programmers + + def _search_network_interface( + self, serial: typing.Optional[str] + ) -> list[Programmer]: + found_programmers = [] + + for p in network_programmers: + name = p.get_name() + + if serial: + p.set_serial(serial) + self.logger.debug(f"Trying {name} with {serial}") + else: + self.logger.debug(f"Trying {name}") + + if p.probe(): + self.logger.debug(f"Found {name}") + found_programmers += [p] + else: + self.logger.debug(f"Failed to probe {name}") + + return found_programmers + + def flash(self): + start_time = time.time() + bin_path = os.path.abspath(self.args.bin) + + if not os.path.exists(bin_path): + self.logger.error(f"Binary file not found: {bin_path}") + return 1 + + if self.args.interface: + i_name = self.args.interface + interfaces = [p for p in programmers if p.get_name() == i_name] + if len(interfaces) == 0: + interfaces = [p for p in network_programmers if p.get_name() == i_name] + else: + self.logger.info(f"Probing for interfaces...") + interfaces = self._search_interface(self.args.serial) + + if len(interfaces) == 0: + # Probe network blackmagic + self.logger.info(f"Probing for network interfaces...") + interfaces = self._search_network_interface(self.args.serial) + + if len(interfaces) == 0: + self.logger.error("No interface found") + return 1 + + if len(interfaces) > 1: + self.logger.error("Multiple interfaces found: ") + self.logger.error( + f"Please specify '--interface={[i.get_name() for i in interfaces]}'" + ) + return 1 + + interface = interfaces[0] + + if self.args.serial: + interface.set_serial(self.args.serial) + self.logger.info( + f"Flashing {bin_path} via {interface.get_name()} with {self.args.serial}" + ) + else: + self.logger.info(f"Flashing {bin_path} via {interface.get_name()}") + + if not interface.flash(bin_path): + self.logger.error(f"Failed to flash via {interface.get_name()}") + return 1 + + flash_time = time.time() - start_time + bin_size = os.path.getsize(bin_path) + self.logger.info(f"Flashed successfully in {flash_time:.2f}s") + self.logger.info(f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s") + return 0 + + +if __name__ == "__main__": + Main()() From 79d45c97f93d54b0082519d7a6a7e9a77e579406 Mon Sep 17 00:00:00 2001 From: nas Date: Mon, 6 Feb 2023 20:24:05 +0500 Subject: [PATCH 08/75] AleX83Xpert/add f keys to keyboard layout (#2362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Assets: png images for F1..F12 keys: Will be used for HID keyboard layout * HID KEYBOARD F1..F12 keys were added - a new row with functional keys was added - logic for displaying the keyboard layout was improved * HidApp: hid functional keys by default Co-authored-by: あく --- .../plugins/hid_app/assets/ButtonF10_5x8.png | Bin 0 -> 172 bytes .../plugins/hid_app/assets/ButtonF11_5x8.png | Bin 0 -> 173 bytes .../plugins/hid_app/assets/ButtonF12_5x8.png | Bin 0 -> 180 bytes .../plugins/hid_app/assets/ButtonF1_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF2_5x8.png | Bin 0 -> 179 bytes .../plugins/hid_app/assets/ButtonF3_5x8.png | Bin 0 -> 178 bytes .../plugins/hid_app/assets/ButtonF4_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF5_5x8.png | Bin 0 -> 178 bytes .../plugins/hid_app/assets/ButtonF6_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF7_5x8.png | Bin 0 -> 176 bytes .../plugins/hid_app/assets/ButtonF8_5x8.png | Bin 0 -> 176 bytes .../plugins/hid_app/assets/ButtonF9_5x8.png | Bin 0 -> 179 bytes .../plugins/hid_app/views/hid_keyboard.c | 28 ++++++++++++++++-- 13 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 applications/plugins/hid_app/assets/ButtonF10_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF11_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF12_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF1_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF2_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF3_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF4_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF5_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF6_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF7_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF8_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF9_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF10_5x8.png b/applications/plugins/hid_app/assets/ButtonF10_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d1a7a04f06dcc4b5446aad5a40a9863038bf56b5 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA~jDJ#}JO|$tewu|Ns9tHZU+a6e)0^L#NYq9;5F(PTxtKzKT}z3_A)0e;QviHwNlp N@O1TaS?83{1OPU#E%g8Z literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF11_5x8.png b/applications/plugins/hid_app/assets/ButtonF11_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7e177358e81695342f2a283a220c7cacc7bda939 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOpBPSmNg(OQ{BTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh8~Mb6 ziqt(_978y+C#N(t{{R2q*ucQxP^7?t4xLU{IYmyznHN-MN(3-k$uq=X7x{LAUCkJ% Og~8L+&t;ucLK6T7Y%hHP literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF12_5x8.png b/applications/plugins/hid_app/assets/ButtonF12_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..50d2a7dc63b9d366ccfbacbc05e6bb0d9e335b5b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk)EfEV+hCf+#W|R1_Pc$J^%l2ww|&zRcE|OcGEe{j literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF1_5x8.png b/applications/plugins/hid_app/assets/ButtonF1_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7394d27105fd0a27067803bfd633a26bedd0f1d5 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|$tewu|Ns9tHZU+a6e)0^L+9jy0|#0HPM+X+s?4mGa`wiPi$55Iw(~PB TTpxE5sExtX)z4*}Q$iB}`k6I% literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF2_5x8.png b/applications/plugins/hid_app/assets/ButtonF2_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..9d922a3858147116d65b6f03e2b36ea846b2f60c GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk*=qUV+hCf)NV&U1_ho&w~qX;m*hWYIaS!#v1}4USoB8kx*gxNJa@^Tf4zarr_o#d UH)s04hd_-Cp00i_>zopr0BX-NbN~PV literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF3_5x8.png b/applications/plugins/hid_app/assets/ButtonF3_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..95c2dd4f4198e182a1a62927c4d3627400a7b883 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk&dT}V+hCf)GkXd1_g%0LLdIeuWPOlF*aSY$&;z#+9C5#-hV?Uh3H=_oX_AhhhO~n UO!>#_f%+IcUHx3vIVCg!073*Z7ytkO literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF4_5x8.png b/applications/plugins/hid_app/assets/ButtonF4_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..602466f4b667b6df4d5335517bd9d43e5f0b6e69 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|vE3Va85}s64*o6Qr{DAJBNr3n8pa7vN_+nsOQj%x4ZT`lf}PS TtvtgA)W+cH>gTe~DWM4fb!sy& literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF5_5x8.png b/applications/plugins/hid_app/assets/ButtonF5_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d73b5405275f6c53f7a2f157df063db1ca351caa GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOFVdQ&MBb@0Fom!%>V!Z literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF6_5x8.png b/applications/plugins/hid_app/assets/ButtonF6_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..c50748257ab8e06f90007e93b913d5be4999d096 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|shy5|3<^BWD?a{@Kh_*L{p89i1p$qBwtDvdG5Wpwnq5@=JeG)jb@A^; T#rkJ}+88`t{an^LB{Ts5$FwwN literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF7_5x8.png b/applications/plugins/hid_app/assets/ButtonF7_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..396c98f5104f94b6310593ce6c7e6ce3d2369ef3 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO+nf~Hxmr&P}udD~(3jVRo RuLQY`!PC{xWt~$(69B*FH3|R# literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF8_5x8.png b/applications/plugins/hid_app/assets/ButtonF8_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..6304d7fb888b2cf38c54e7124aaa604d1610629c GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA}voB#}JO|wVjT93<^BWEB^mCmh0Jd#>H=GOE1@xHLh7tre2KydVRe*qgvi_@vq`% Sf29C*F?hQAxvXEZzkxv|` zNY~TFF@)oKa!Nzv|NsAu4GatpMG73~&^g&~GSNx=@BjbyU3DUS%F5hxSQ8l-I!}xL U>{@7g2&j?4)78&qol`;+0Ic0IyZ`_I literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c index 8b12e8fd1..82e772b15 100644 --- a/applications/plugins/hid_app/views/hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -46,11 +46,25 @@ typedef struct { #define KEY_WIDTH 9 #define KEY_HEIGHT 12 #define KEY_PADDING 1 -#define ROW_COUNT 6 +#define ROW_COUNT 7 #define COLUMN_COUNT 12 // 0 width items are not drawn, but there value is used const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { + { + {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, + {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2}, + {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3}, + {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4}, + {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5}, + {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6}, + {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7}, + {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8}, + {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9}, + {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10}, + {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11}, + {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, + }, { {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, @@ -224,7 +238,12 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontKeyboard); // Start shifting the all keys up if on the next row (Scrolling) - uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; + uint8_t initY = model->y == 0 ? 0 : 1; + + if(model->y > 5) { + initY = model->y - 4; + } + for(uint8_t y = initY; y < ROW_COUNT; y++) { const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; uint8_t x = 0; @@ -365,7 +384,10 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { with_view_model( hid_keyboard->view, HidKeyboardModel * model, - { model->transport = bt_hid->transport; }, + { + model->transport = bt_hid->transport; + model->y = 1; + }, true); return hid_keyboard; From 147f42a2b7bbcd5a497e862be47a72ef2c233801 Mon Sep 17 00:00:00 2001 From: Wild Rat Date: Mon, 6 Feb 2023 17:49:34 +0200 Subject: [PATCH 09/75] Add Daikin FTXM20M & Mitsubishi SRK63HE (#2349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- assets/resources/infrared/assets/ac.ir | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 1f9c2145f..96a2a0f38 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -297,3 +297,77 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 +# Model: Daikin FTXM20M. +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 393 473 392 473 368 498 368 497 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1263 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1268 464 1269 463 1269 463 1292 440 1269 463 404 462 426 439 1293 439 426 440 426 440 426 440 426 440 426 440 427 439 426 440 427 439 427 438 427 439 1294 438 427 439 1294 438 427 439 427 439 428 438 1294 438 1294 438 428 438 428 437 428 438 428 438 1294 438 428 438 428 438 429 437 429 437 429 436 429 437 429 437 429 437 429 436 429 437 430 436 1296 436 1296 436 1296 436 430 436 430 435 1297 435 1297 435 1298 434 35482 3500 1699 464 1268 464 402 463 402 463 403 463 1269 463 426 440 426 439 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1294 438 427 438 427 439 1294 438 427 439 428 438 428 438 428 438 427 438 428 438 428 437 428 438 428 437 428 438 428 438 1295 437 429 437 429 437 429 436 429 437 1296 436 429 437 429 436 429 437 430 436 430 436 430 435 430 436 430 435 431 435 431 435 431 435 455 410 456 409 1322 410 456 409 456 410 456 410 456 410 456 410 1323 409 457 409 457 409 1323 409 1323 409 457 409 35483 3500 1699 464 1268 464 402 464 402 463 403 463 1269 463 426 440 426 440 426 440 426 439 1293 439 426 439 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1294 438 1293 438 427 439 427 438 1294 438 428 438 427 438 428 438 428 438 428 438 428 437 428 438 428 438 428 438 428 438 428 438 428 438 429 437 429 437 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 430 436 1297 435 430 436 430 436 431 435 431 435 456 410 456 410 456 410 456 409 1323 409 1322 410 456 410 456 409 457 409 457 409 457 409 457 409 457 408 457 409 1324 408 1324 408 1324 408 1324 408 458 408 1325 406 459 407 1351 356 1376 356 1376 356 1376 356 1377 355 510 355 510 356 511 355 511 354 511 355 537 328 537 329 538 328 538 327 538 328 539 327 565 300 566 300 1432 300 1433 298 593 272 594 271 621 245 621 244 622 243 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 368 498 369 497 367 499 366 499 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1264 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1269 463 1269 463 1269 463 1292 440 1292 440 426 440 426 440 1293 439 426 440 426 439 426 440 426 440 427 439 427 438 427 439 427 439 427 439 427 439 1293 439 427 439 1294 438 427 439 427 439 428 437 1294 438 1294 438 428 437 428 438 428 437 429 437 1295 437 428 437 429 437 429 437 429 436 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 1297 435 1297 435 431 435 455 410 1298 434 1322 410 1322 410 35482 3500 1700 464 1269 463 403 463 403 463 403 462 1292 440 426 440 426 440 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1293 439 427 439 427 439 1294 438 427 439 427 439 427 439 428 438 427 438 428 438 428 438 428 437 428 438 428 438 428 438 1295 437 429 437 429 436 429 437 429 437 1296 436 429 437 429 437 429 437 430 436 430 436 430 436 431 435 431 435 431 435 455 410 456 410 456 410 456 410 1322 410 456 410 456 410 456 409 457 409 456 409 1323 409 457 409 457 409 1323 409 1324 408 458 408 35483 3500 1700 464 1268 464 402 464 403 463 402 464 1292 440 426 440 426 440 426 439 426 440 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 438 1293 439 1293 439 427 439 427 438 1294 438 428 438 428 438 427 438 428 438 428 438 428 438 428 438 428 438 428 438 428 437 428 438 429 436 429 437 429 437 429 437 429 437 429 437 429 437 1296 436 430 435 430 436 1297 435 431 435 1297 435 431 435 456 409 456 410 456 409 456 410 456 410 456 410 456 410 1323 409 1323 409 457 409 457 409 457 408 457 409 457 409 458 408 458 407 458 408 1325 407 1326 405 1327 406 1351 381 485 356 1376 356 510 355 1377 355 1377 355 1377 355 1378 354 1378 354 512 354 512 354 538 327 538 328 538 328 539 326 565 300 566 300 566 299 567 299 593 272 621 244 596 270 1488 244 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 504 389 477 364 501 364 502 364 501 364 502 25048 3511 1684 505 1228 504 389 475 390 474 392 472 1260 470 396 469 396 470 396 469 397 469 1263 469 397 469 1263 469 1263 469 397 468 1263 469 1263 469 1264 468 1263 469 1264 468 397 468 398 468 1264 468 398 468 398 468 398 468 398 468 398 468 399 467 398 468 399 467 423 443 400 466 1289 443 423 443 1289 443 423 443 423 443 423 443 1289 443 1290 442 424 442 423 442 423 443 424 442 1290 442 424 442 423 443 424 442 424 441 424 442 424 441 424 442 424 442 424 441 424 442 424 442 1291 441 1291 441 1291 441 425 441 425 441 1291 441 1291 441 1291 441 35478 3505 1695 468 1264 468 398 467 398 468 398 468 1265 467 398 467 398 468 398 468 399 467 1265 467 399 467 1266 466 1266 466 423 443 1289 443 1290 442 1290 442 1290 442 1290 442 424 442 423 442 1290 442 423 443 424 442 424 442 424 442 424 441 424 442 424 442 424 441 424 442 424 442 424 442 1291 441 425 441 425 441 424 442 424 441 1291 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 440 426 440 426 440 1292 440 426 440 426 440 426 440 426 439 427 439 1293 439 427 439 427 438 1294 438 1294 438 428 437 35479 3504 1695 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 466 399 467 1265 467 399 467 1289 443 1290 442 423 443 1290 442 1290 442 1290 442 1290 442 1290 442 423 443 424 442 1290 442 424 442 424 442 424 441 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1291 441 425 441 425 441 1292 440 1292 440 1292 440 426 440 425 441 426 440 426 440 1292 440 426 440 426 440 1292 440 426 440 426 440 426 440 427 439 426 440 426 440 427 438 427 439 427 439 427 439 1294 438 1295 437 1294 438 1319 413 453 413 1319 413 453 412 1320 412 1319 413 1319 413 1319 413 1320 412 453 412 453 413 453 412 454 412 454 411 454 412 454 412 454 412 454 412 454 412 455 411 454 412 455 411 1321 411 1321 411 456 409 456 410 481 384 482 384 482 383 482 359 506 360 507 359 507 359 507 358 1374 358 1374 358 508 357 508 358 509 357 534 332 534 332 534 332 534 332 535 331 535 331 535 331 535 330 536 330 536 330 562 303 563 303 563 302 564 302 1430 302 565 301 564 302 591 274 619 247 619 247 1512 219 1539 190 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 502 390 476 365 501 365 500 365 500 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 469 397 468 398 468 398 468 398 468 1264 468 398 468 1265 467 1265 467 399 467 1265 467 1265 467 1265 467 1266 466 1265 467 400 466 401 465 1266 466 401 465 401 465 401 465 424 442 424 441 424 442 424 441 424 442 424 442 424 442 1291 441 424 442 1291 441 424 442 425 440 425 441 1291 441 1291 441 425 441 425 441 425 440 425 441 1292 440 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 1293 439 1293 439 426 439 427 439 1293 439 1293 439 1293 439 35480 3503 1696 467 1265 467 399 467 398 468 399 466 1266 466 399 467 399 467 399 467 399 467 1266 466 400 466 1267 465 1290 442 424 442 1290 442 1290 442 1290 442 1290 441 1291 441 424 442 424 442 1291 441 424 442 424 442 425 441 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 440 426 440 1292 440 426 440 426 439 426 440 426 439 426 440 426 440 426 440 426 440 426 440 427 438 427 439 427 439 427 439 1293 439 427 438 427 439 428 438 428 438 428 438 1294 438 428 438 428 438 1295 437 1320 412 453 413 35480 3503 1696 467 1265 467 399 467 399 467 399 467 1266 466 399 467 399 467 400 466 400 466 1290 442 424 442 1289 443 1290 442 424 442 1291 441 1290 442 1290 442 1291 441 1291 441 424 442 424 442 1291 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 427 439 426 439 427 439 1293 439 1293 439 1293 439 427 438 1294 438 427 439 427 438 428 438 427 438 453 413 429 437 429 437 429 437 453 412 454 412 1320 412 1320 412 1320 412 1320 412 454 412 1321 411 454 412 1320 412 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 411 455 411 456 410 456 410 457 409 481 384 457 409 482 383 483 383 483 358 1374 358 1374 358 508 357 508 358 509 357 509 357 509 357 509 357 510 356 535 330 536 330 536 330 1402 330 1403 329 537 329 536 329 563 302 564 302 564 302 565 300 565 300 592 273 619 246 620 246 619 246 620 245 674 188 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 504 365 500 365 500 365 500 365 501 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 470 397 468 397 469 398 468 398 468 1264 468 398 467 1265 467 1265 467 398 468 1265 467 1265 467 1265 467 1266 466 1266 466 399 466 399 467 1266 466 400 466 400 466 400 465 400 466 424 442 424 442 424 442 424 441 424 442 424 442 1291 441 424 442 1291 441 424 442 424 442 425 441 1291 441 1291 441 425 440 425 441 425 441 425 440 1292 440 425 441 425 440 425 441 425 441 425 441 425 441 425 441 426 440 426 440 426 440 426 439 1293 439 1293 439 1293 439 426 440 427 439 1293 439 1293 439 1293 439 35480 3503 1696 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 467 399 467 1267 465 400 466 1266 466 1267 465 424 441 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 442 1291 441 424 442 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 441 425 441 1292 440 426 440 426 439 426 440 426 440 426 439 426 440 426 440 426 439 427 439 426 440 427 439 427 439 427 438 1293 439 427 439 427 439 427 439 428 438 428 438 1295 437 428 438 429 436 1296 436 1296 436 453 412 35481 3502 1696 467 1265 467 399 466 399 467 399 467 1265 467 399 467 399 467 399 466 400 466 1266 466 400 466 1290 442 1267 465 424 442 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 441 1291 441 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 425 441 425 441 425 441 426 440 1292 440 426 440 426 440 1292 440 426 440 426 439 1293 439 427 439 427 439 427 439 1293 439 1294 438 1294 438 1293 439 427 439 428 438 428 438 428 438 429 436 429 437 429 437 453 412 453 413 453 412 1320 412 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1322 410 455 411 455 411 456 410 456 410 455 410 456 410 456 409 481 384 458 408 482 383 482 384 483 358 508 358 1374 358 1374 358 508 357 509 357 509 357 510 355 535 330 536 330 536 330 536 329 536 330 536 330 1403 329 1430 301 564 302 564 302 564 301 591 274 566 301 592 273 619 247 619 247 620 245 647 219 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189 +# Model: Mitsubishi SRK63HE. +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3232 1526 463 334 462 1127 464 334 460 334 461 1128 463 336 459 1128 464 359 436 359 436 1133 458 1156 434 1157 434 362 433 1159 432 363 432 1159 432 1159 432 1159 432 363 433 363 432 363 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 363 432 364 432 1160 431 363 432 363 432 1160 432 363 432 364 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 432 364 432 364 431 364 431 1160 432 364 432 364 431 364 431 1160 431 1160 431 1160 431 364 431 1161 430 1161 430 1160 431 1161 430 364 432 364 431 365 431 1161 430 364 431 365 431 364 431 365 430 365 431 1161 430 1161 430 1161 430 365 430 1161 430 365 430 365 430 1161 430 365 430 365 431 365 430 1161 430 365 430 1161 430 1161 430 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3229 1528 461 335 461 1128 463 334 461 335 460 1129 463 337 458 1129 463 359 436 360 434 1156 434 1157 433 1159 432 364 431 1161 430 364 431 1161 430 1161 430 1160 431 364 431 364 431 365 430 365 430 1161 430 1161 430 365 430 1161 430 1161 431 365 430 365 431 1161 430 365 430 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 1161 430 1161 430 1161 430 1161 430 1161 431 1161 430 365 430 1161 430 1161 430 1161 431 365 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 1162 429 1162 429 1162 430 365 430 1162 429 1162 429 1162 429 1162 429 366 430 366 429 366 430 1162 429 366 429 366 430 366 429 366 429 1162 429 366 429 1163 428 366 429 1162 429 366 429 366 429 1162 429 366 430 1162 429 366 429 1163 429 366 430 1163 428 1163 428 367 428 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3230 1526 463 335 460 1128 464 333 461 335 460 1128 463 337 458 1128 463 359 436 359 436 1155 435 1157 433 1158 432 363 432 1160 431 364 431 1161 430 1160 431 1160 431 364 431 364 431 364 431 364 431 1161 430 1161 430 364 431 1161 430 1160 431 365 430 364 431 1161 431 364 431 364 431 1161 430 365 430 365 431 1161 430 1161 431 364 431 1161 430 1161 430 1161 430 1161 431 1161 430 1161 431 365 430 1161 431 1161 430 1162 430 365 430 365 430 365 431 365 430 1161 430 365 430 365 430 365 431 1161 430 1162 430 1162 429 365 430 1162 429 1162 429 1162 429 1162 429 366 429 366 430 366 430 1162 429 366 430 366 429 366 429 366 430 366 429 1162 429 1163 429 366 429 366 429 1163 428 1163 428 1163 429 1163 428 366 429 366 430 1163 428 1163 428 367 428 367 429 366 429 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3231 1526 463 334 461 1127 465 333 461 334 460 1128 463 336 459 1129 463 359 436 359 436 1133 458 1156 434 1157 433 362 433 1159 432 363 432 1159 432 1160 431 1159 433 363 432 363 432 363 432 363 432 1159 433 1159 432 363 432 1159 432 1160 432 363 432 363 432 1159 432 363 432 363 433 1159 432 364 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 1160 431 1160 432 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 364 431 364 431 364 432 364 431 1160 432 364 431 364 431 364 432 1160 431 1161 430 1161 430 364 431 1161 431 1161 430 1161 430 1161 430 364 432 364 431 364 431 1161 430 365 430 365 430 365 430 365 431 365 430 1161 430 1161 431 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 365 430 1162 430 365 431 1161 430 1162 429 365 430 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3230 1526 463 334 461 1128 464 333 461 334 461 1128 463 336 459 1129 463 359 436 359 436 1155 435 1156 434 1158 433 363 432 1159 432 363 432 1159 432 1160 431 1160 431 363 433 363 432 363 432 363 432 1160 432 1160 431 364 431 1160 431 1160 432 364 431 364 431 1160 431 364 431 364 432 1160 431 364 431 364 431 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 432 1160 431 1160 431 364 432 364 431 364 431 364 431 1161 430 364 431 364 432 364 431 1161 430 1161 430 1161 430 365 430 1161 431 1161 430 1161 430 1161 430 365 430 365 430 365 430 1161 430 365 431 365 431 365 430 365 431 1161 430 1161 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 365 430 1162 429 1162 430 1162 429 366 429 1162 429 1162 429 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430 From 05ddeb3066190f2397ee0806a46ab40d035e060b Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 19:28:26 +0000 Subject: [PATCH 10/75] Alpha sort asset packs setting --- .../scenes/xtreme_settings_scene_start.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 53d679efb..f3e41ba70 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -145,9 +145,20 @@ void xtreme_settings_scene_start_on_enter(void* context) { if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); - asset_packs_push_back(app->asset_packs, copy); - if(strcmp(name, xtreme_settings->asset_pack) == 0) - current_pack = asset_packs_size(app->asset_packs); + uint idx; + for(idx = 0; idx < asset_packs_size(app->asset_packs); idx++) { + if(strcasecmp(copy, *asset_packs_get(app->asset_packs, idx)) < 0) { + break; + } + } + asset_packs_push_at(app->asset_packs, idx, copy); + if(current_pack != 0) { + if(idx <= current_pack) + current_pack++; + } else { + if(strcmp(copy, xtreme_settings->asset_pack) == 0) + current_pack = idx + 1; + } } } } while(false); From d6dcf0efa26f77f99d9aac9cba00ad6636e1ebc4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 20:51:07 +0000 Subject: [PATCH 11/75] Case insensitive file browser list --- applications/main/archive/helpers/archive_files.h | 2 +- applications/services/gui/modules/file_browser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index db624f5b5..881d1f134 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -88,7 +88,7 @@ static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { return -1; } - return furi_string_cmp(a->path, b->path); + return furi_string_cmpi(a->path, b->path); } #define M_OPL_ArchiveFile_t() \ diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 9d65df46b..5d87fcb45 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -84,7 +84,7 @@ static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { return -1; } - return furi_string_cmp(a->path, b->path); + return furi_string_cmpi(a->path, b->path); } #define M_OPL_BrowserItem_t() \ From e393285064aaa6d2264e25e29f6c1db1f9f76080 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 23:05:34 +0000 Subject: [PATCH 12/75] Fix file browser long list jump bug --- .../gui/modules/file_browser_worker.c | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4386fdfd0..d4e83aefb 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -203,55 +203,57 @@ static bool uint32_t items_cnt = 0; + bool ret = false; do { if(!storage_dir_open(directory, furi_string_get_cstr(path))) { break; } - items_cnt = 0; - while(items_cnt < offset) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - items_cnt++; - } - } else { - break; - } - } - if(items_cnt != offset) { - break; - } + // items_cnt = 0; + // while(items_cnt < offset) { + // if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + // break; + // } + // if(storage_file_get_error(directory) == FSE_OK) { + // furi_string_set(name_str, name_temp); + // if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + // items_cnt++; + // } + // } else { + // break; + // } + // } + // if(items_cnt != offset) { + // break; + // } + // FLIPPER DEVS MOMENT + // this used to load the file list in chunks, and then sort it... + // so while scrolling, it loads more files and sorts them... + // chances are, the new files are higner in the sorted list... + // so the files keep shifting around while scrolling... + // now this does something intelligent and loads all in one go. + // might take a few milliseconds longer, but atleast it works :kekw: + UNUSED(offset); + UNUSED(count); if(browser->list_load_cb) { - browser->list_load_cb(browser->cb_ctx, offset); + browser->list_load_cb(browser->cb_ctx, 0); } - - items_cnt = 0; - while(items_cnt < count) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); - if(browser->list_item_cb) { - browser->list_item_cb( - browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); - } - items_cnt++; + while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && storage_file_get_error(directory) == FSE_OK) { + furi_string_set(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); + if(browser->list_item_cb) { + browser->list_item_cb( + browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); } - } else { - break; + items_cnt++; } } if(browser->list_item_cb) { browser->list_item_cb(browser->cb_ctx, NULL, false, true); } + ret = true; } while(0); furi_string_free(name_str); @@ -261,7 +263,7 @@ static bool furi_record_close(RECORD_STORAGE); - return (items_cnt == count); + return ret; } static int32_t browser_worker(void* context) { From e98ad6fc2c2bcce58b3b5e726286fb56b2abeef0 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 00:24:19 +0000 Subject: [PATCH 13/75] Fix file browser sorting for good --- applications/main/archive/helpers/archive_files.h | 3 +++ applications/services/gui/modules/file_browser.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 881d1f134..27f6353a7 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -87,6 +87,9 @@ static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { return -1; } + if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { + return 1; + } return furi_string_cmpi(a->path, b->path); } diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 5d87fcb45..eecea0dbd 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -78,11 +78,18 @@ static void BrowserItem_t_clear(BrowserItem_t* obj) { static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { // Back indicator comes before everything, then folders, then all other files. - if((a->type == BrowserItemTypeBack) || - (a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder && - b->type != BrowserItemTypeBack)) { + if(a->type == BrowserItemTypeBack) { return -1; } + if(b->type == BrowserItemTypeBack) { + return 1; + } + if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { + return -1; + } + if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { + return 1; + } return furi_string_cmpi(a->path, b->path); } From edd3171d6c2a8895a7f1f8ac4375891bf3d45d56 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 00:50:38 +0000 Subject: [PATCH 14/75] Add xfw setting for sort dirs before files --- applications/main/archive/helpers/archive_files.h | 13 ++++++++----- applications/services/gui/modules/file_browser.c | 13 ++++++++----- .../scenes/xtreme_settings_scene_start.c | 13 +++++++++++++ .../settings/xtreme_settings/xtreme_settings.h | 1 + 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 27f6353a7..a9c33c9f6 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -6,6 +6,7 @@ #include #include #include "toolbox/path.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define FAP_MANIFEST_MAX_ICON_SIZE 32 @@ -84,11 +85,13 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) { } static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { - if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { - return -1; - } - if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { - return 1; + if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { + return -1; + } + if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { + return 1; + } } return furi_string_cmpi(a->path, b->path); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index eecea0dbd..c6971c82a 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -11,6 +11,7 @@ #include #include #include "toolbox/path.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define LIST_ITEMS 5u #define MAX_LEN_PX 110 @@ -84,11 +85,13 @@ static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { if(b->type == BrowserItemTypeBack) { return 1; } - if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { - return -1; - } - if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { - return 1; + if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { + return -1; + } + if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { + return 1; + } } return furi_string_cmpi(a->path, b->path); diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index f3e41ba70..d6532af2c 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -87,6 +87,14 @@ static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item app->settings_changed = true; } +static void xtreme_settings_scene_start_sort_folders_before_changed(VariableItem* item) { + XtremeSettingsApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->sort_ignore_dirs = !value; + app->settings_changed = true; +} + static void xtreme_settings_scene_start_xp_level_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); app->dolphin_level = variable_item_get_current_value_index(item) + 1; @@ -219,6 +227,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, battery_style_names[value_index]); + item = variable_item_list_add( + var_item_list, "Sort folders before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); + variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); + char level_str[4]; snprintf(level_str, 4, "%i", app->dolphin_level); item = variable_item_list_add( diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index 0a19ca643..ee0ec5583 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -22,6 +22,7 @@ typedef struct { char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; + bool sort_ignore_dirs; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); From d3cf757eade9c4e16ce392f745851db4759278d1 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:08:05 +0000 Subject: [PATCH 15/75] Fix typos --- applications/services/gui/modules/file_browser_worker.c | 2 +- .../xtreme_settings/scenes/xtreme_settings_scene_start.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index d4e83aefb..6cbf4b3e8 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -230,7 +230,7 @@ static bool // FLIPPER DEVS MOMENT // this used to load the file list in chunks, and then sort it... // so while scrolling, it loads more files and sorts them... - // chances are, the new files are higner in the sorted list... + // chances are, the new files are higher in the sorted list... // so the files keep shifting around while scrolling... // now this does something intelligent and loads all in one go. // might take a few milliseconds longer, but atleast it works :kekw: diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index d6532af2c..c0f2262fd 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -228,7 +228,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, battery_style_names[value_index]); item = variable_item_list_add( - var_item_list, "Sort folders before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + var_item_list, "Sort dirs before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); From 3bd3b7f122b1ae798a5c713f3777eb3a66ed8985 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:08:43 +0000 Subject: [PATCH 16/75] Format --- .../services/gui/modules/file_browser_worker.c | 3 ++- .../scenes/xtreme_settings_scene_start.c | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 6cbf4b3e8..0ae0907de 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -239,7 +239,8 @@ static bool if(browser->list_load_cb) { browser->list_load_cb(browser->cb_ctx, 0); } - while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && storage_file_get_error(directory) == FSE_OK) { + while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && + storage_file_get_error(directory) == FSE_OK) { furi_string_set(name_str, name_temp); if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index c0f2262fd..03d75def7 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -161,11 +161,9 @@ void xtreme_settings_scene_start_on_enter(void* context) { } asset_packs_push_at(app->asset_packs, idx, copy); if(current_pack != 0) { - if(idx <= current_pack) - current_pack++; + if(idx <= current_pack) current_pack++; } else { - if(strcmp(copy, xtreme_settings->asset_pack) == 0) - current_pack = idx + 1; + if(strcmp(copy, xtreme_settings->asset_pack) == 0) current_pack = idx + 1; } } } @@ -228,7 +226,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, battery_style_names[value_index]); item = variable_item_list_add( - var_item_list, "Sort dirs before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + var_item_list, + "Sort dirs before", + 2, + xtreme_settings_scene_start_sort_folders_before_changed, + app); variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); From 497f369d16076c39a98b8fceebf0084642ca06f4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:59:40 +0000 Subject: [PATCH 17/75] Fix asset pack setting sorting --- .../xtreme_settings/scenes/xtreme_settings_scene_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 03d75def7..3323cf1df 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -161,7 +161,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { } asset_packs_push_at(app->asset_packs, idx, copy); if(current_pack != 0) { - if(idx <= current_pack) current_pack++; + if(idx < current_pack) current_pack++; } else { if(strcmp(copy, xtreme_settings->asset_pack) == 0) current_pack = idx + 1; } From 1ff5843ee690e3e6c0d74a5fa569de3ffcf2b8d1 Mon Sep 17 00:00:00 2001 From: Round-Pi <9893098+Round-Pi@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:05:52 -0500 Subject: [PATCH 18/75] battery info temperature shown in C or F based on settings (#2360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * battery Info temperature displays C or F * PowerSettings: add locale module to dependencies Co-authored-by: あく --- .../settings/power_settings_app/application.fam | 1 + .../settings/power_settings_app/views/battery_info.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/applications/settings/power_settings_app/application.fam b/applications/settings/power_settings_app/application.fam index a5b1966b2..6f3589e1a 100644 --- a/applications/settings/power_settings_app/application.fam +++ b/applications/settings/power_settings_app/application.fam @@ -6,6 +6,7 @@ App( requires=[ "gui", "power", + "locale", ], flags=["InsomniaSafe"], stack_size=1 * 1024, diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 5353a2e2a..d29769d21 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -2,6 +2,7 @@ #include #include #include +#include #define LOW_CHARGE_THRESHOLD 10 #define HIGH_DRAIN_CURRENT_THRESHOLD 100 @@ -101,7 +102,15 @@ static void battery_info_draw_callback(Canvas* canvas, void* context) { char health[10]; snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); - snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + if(locale_get_measurement_unit() == LocaleMeasurementUnitsMetric) { + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + } else { + snprintf( + temperature, + sizeof(temperature), + "%lu F", + (uint32_t)locale_celsius_to_fahrenheit(model->gauge_temperature)); + } snprintf( voltage, sizeof(voltage), From d035872cf657eccc7a488c61e4cbf1850ab0bf7c Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Tue, 7 Feb 2023 04:21:25 +0100 Subject: [PATCH 19/75] nfc: Add mifare classic value block commands (#2317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: gornekich Co-authored-by: あく --- firmware/targets/f7/api_symbols.csv | 11 +- lib/nfc/protocols/mifare_classic.c | 555 +++++++++++++++++++++++----- lib/nfc/protocols/mifare_classic.h | 37 ++ lib/nfc/protocols/nfc_util.c | 25 +- lib/nfc/protocols/nfc_util.h | 4 +- 5 files changed, 541 insertions(+), 91 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 02f476cc0..14036ef1a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1863,8 +1863,10 @@ Function,+,menu_reset,void,Menu* Function,+,menu_set_selected_item,void,"Menu*, uint32_t" Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, MfClassicAuthContext*, uint64_t" Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" +Function,-,mf_classic_auth_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" Function,-,mf_classic_authenticate_skip_activate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey, _Bool, uint32_t" +Function,-,mf_classic_block_to_value,_Bool,"const uint8_t*, int32_t*, uint8_t*" Function,-,mf_classic_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" Function,-,mf_classic_dict_add_key,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_add_key_str,_Bool,"MfClassicDict*, FuriString*" @@ -1891,6 +1893,7 @@ Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfCl Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType Function,-,mf_classic_get_type_str,const char*,MfClassicType +Function,-,mf_classic_halt,void,"FuriHalNfcTxRxContext*, Crypto1*" Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" Function,-,mf_classic_is_allowed_access_sector_trailer,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" Function,-,mf_classic_is_block_read,_Bool,"MfClassicData*, uint8_t" @@ -1899,6 +1902,8 @@ Function,-,mf_classic_is_key_found,_Bool,"MfClassicData*, uint8_t, MfClassicKey" Function,-,mf_classic_is_sector_data_read,_Bool,"MfClassicData*, uint8_t" Function,-,mf_classic_is_sector_read,_Bool,"MfClassicData*, uint8_t" Function,-,mf_classic_is_sector_trailer,_Bool,uint8_t +Function,-,mf_classic_is_value_block,_Bool,"MfClassicData*, uint8_t" +Function,-,mf_classic_read_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" Function,-,mf_classic_read_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicReader*, MfClassicData*" Function,-,mf_classic_read_sector,void,"FuriHalNfcTxRxContext*, MfClassicData*, uint8_t" Function,-,mf_classic_reader_add_sector,void,"MfClassicReader*, uint8_t, uint64_t, uint64_t" @@ -1906,8 +1911,12 @@ Function,-,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlo Function,-,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKey, uint64_t" Function,-,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKey" Function,-,mf_classic_set_sector_data_not_read,void,MfClassicData* +Function,-,mf_classic_transfer,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t" Function,-,mf_classic_update_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicData*" -Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" +Function,-,mf_classic_value_cmd,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, uint8_t, int32_t" +Function,-,mf_classic_value_cmd_full,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t, int32_t" +Function,-,mf_classic_value_to_block,void,"int32_t, uint8_t, uint8_t*" +Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" Function,-,mf_classic_write_sector,_Bool,"FuriHalNfcTxRxContext*, MfClassicData*, MfClassicData*, uint8_t" Function,-,mf_df_cat_application,void,"MifareDesfireApplication*, FuriString*" Function,-,mf_df_cat_application_info,void,"MifareDesfireApplication*, FuriString*" diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 5887ab4c1..28667f09b 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -7,10 +7,17 @@ #define TAG "MfClassic" -#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) -#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_BLOCK_CMD (0x30) -#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) +#define MF_CLASSIC_ACK_CMD 0xAU +#define MF_CLASSIC_NACK_BUF_VALID_CMD 0x0U +#define MF_CLASSIC_NACK_BUF_INVALID_CMD 0x4U +#define MF_CLASSIC_AUTH_KEY_A_CMD 0x60U +#define MF_CLASSIC_AUTH_KEY_B_CMD 0x61U +#define MF_CLASSIC_READ_BLOCK_CMD 0x30U +#define MF_CLASSIC_WRITE_BLOCK_CMD 0xA0U +#define MF_CLASSIC_TRANSFER_CMD 0xB0U +#define MF_CLASSIC_DECREMENT_CMD 0xC0U +#define MF_CLASSIC_INCREMENT_CMD 0xC1U +#define MF_CLASSIC_RESTORE_CMD 0xC2U const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicTypeMini) { @@ -217,8 +224,8 @@ void mf_classic_get_read_sectors_and_keys( uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); bool blocks_read = true; - for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) { - blocks_read = mf_classic_is_block_read(data, i); + for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) { + blocks_read = mf_classic_is_block_read(data, j); if(!blocks_read) break; } if(blocks_read) { @@ -353,6 +360,16 @@ static bool mf_classic_is_allowed_access( } } +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { + // Check if key A can write, if it can, it's transport configuration, not data block + return !mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyA, MfClassicActionDataWrite) && + (mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyB, MfClassicActionDataInc) || + mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyB, MfClassicActionDataDec)); +} + bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { @@ -401,6 +418,36 @@ void mf_classic_reader_add_sector( } } +bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr) { + uint32_t v = *(uint32_t*)&block[0]; + uint32_t v_inv = *(uint32_t*)&block[4]; + uint32_t v1 = *(uint32_t*)&block[8]; + + bool val_checks = + ((v == v1) && (v == ~v_inv) && (block[12] == (~block[13] & 0xFF)) && + (block[14] == (~block[15] & 0xFF)) && (block[12] == block[14])); + if(value) { + *value = (int32_t)v; + } + if(addr) { + *addr = block[12]; + } + return val_checks; +} + +void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { + uint32_t v_inv = ~((uint32_t)value); + + memcpy(block, &value, 4); + memcpy(block + 4, &v_inv, 4); + memcpy(block + 8, &value, 4); + + block[12] = addr; + block[13] = ~addr & 0xFF; + block[14] = addr; + block[15] = ~addr & 0xFF; +} + void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { furi_assert(auth_ctx); auth_ctx->sector = sector; @@ -553,8 +600,9 @@ bool mf_classic_read_block( uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 4 * 9; + crypto1_encrypt( + crypto, NULL, plain_cmd, sizeof(plain_cmd) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = sizeof(plain_cmd) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { @@ -779,6 +827,9 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ bool is_encrypted = false; uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE]; MfClassicKey access_key = MfClassicKeyA; + // Used for decrement and increment - copy to block on transfer + uint8_t transfer_buf[MF_CLASSIC_BLOCK_SIZE] = {}; + bool transfer_buf_valid = false; // Read command while(!command_processed) { //-V654 @@ -797,18 +848,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } - if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { + // After increment, decrement or restore the only allowed command is transfer + uint8_t cmd = plain_data[0]; + if(transfer_buf_valid && cmd != MF_CLASSIC_TRANSFER_CMD) { + break; + } + + if(cmd == 0x50 && plain_data[1] == 0x00) { FURI_LOG_T(TAG, "Halt received"); furi_hal_nfc_listen_sleep(); command_processed = true; break; - } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { + } + if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD || cmd == MF_CLASSIC_AUTH_KEY_B_CMD) { uint8_t block = plain_data[1]; uint64_t key = 0; uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; - if(plain_data[0] == 0x60) { + if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { key = nfc_util_bytes2num(sector_trailer->key_a, 6); access_key = MfClassicKeyA; } else { @@ -826,9 +884,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); memcpy(tx_rx->tx_data, nt, sizeof(nt)); tx_rx->tx_parity[0] = 0; - for(size_t i = 0; i < sizeof(nt); i++) { - tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); - } + nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(nt)); tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { @@ -849,7 +905,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } if(tx_rx->rx_bits != 64) { - FURI_LOG_W(TAG, "Incorrect nr + ar"); + FURI_LOG_W(TAG, "Incorrect nr + ar length: %d", tx_rx->rx_bits); command_processed = true; break; } @@ -857,16 +913,6 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); - FURI_LOG_D( - TAG, - "%08lx key%c block %d nt/nr/ar: %08lx %08lx %08lx", - emulator->cuid, - access_key == MfClassicKeyA ? 'A' : 'B', - sector_trailer_block, - nonce, - nr, - ar); - crypto1_word(&emulator->crypto, nr, 1); uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { @@ -877,22 +923,30 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } uint32_t ans = prng_successor(nonce, 96); - uint8_t responce[4] = {}; - nfc_util_num2bytes(ans, 4, responce); + uint8_t response[4] = {}; + nfc_util_num2bytes(ans, 4, response); crypto1_encrypt( &emulator->crypto, NULL, - responce, - sizeof(responce) * 8, + response, + sizeof(response) * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = sizeof(responce) * 8; + tx_rx->tx_bits = sizeof(response) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; is_encrypted = true; - } else if(is_encrypted && plain_data[0] == 0x30) { + continue; + } + + if(!is_encrypted) { + FURI_LOG_T(TAG, "Invalid command before auth session established: %02X", cmd); + break; + } + + if(cmd == MF_CLASSIC_READ_BLOCK_CMD) { uint8_t block = plain_data[1]; - uint8_t block_data[18] = {}; + uint8_t block_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { if(!mf_classic_is_allowed_access( @@ -927,24 +981,24 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ sizeof(block_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 18 * 8; + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else if(is_encrypted && plain_data[0] == 0xA0) { + } else if(cmd == MF_CLASSIC_WRITE_BLOCK_CMD) { uint8_t block = plain_data[1]; if(block > mf_classic_get_total_block_num(emulator->data.type)) { break; } // Send ACK - uint8_t ack = 0x0A; + uint8_t ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - if(tx_rx->rx_bits != 18 * 8) break; + if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) break; crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); - uint8_t block_data[16] = {}; + uint8_t block_data[MF_CLASSIC_BLOCK_SIZE] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { if(mf_classic_is_allowed_access( @@ -963,6 +1017,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionDataWrite)) { memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); + } else { + break; } } if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != 0) { @@ -970,19 +1026,83 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ emulator->data_changed = true; } // Send ACK - ack = 0x0A; + ack = MF_CLASSIC_ACK_CMD; + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + } else if( + cmd == MF_CLASSIC_DECREMENT_CMD || cmd == MF_CLASSIC_INCREMENT_CMD || + cmd == MF_CLASSIC_RESTORE_CMD) { + uint8_t block = plain_data[1]; + + if(block > mf_classic_get_total_block_num(emulator->data.type)) { + break; + } + + MfClassicAction action = MfClassicActionDataDec; + if(cmd == MF_CLASSIC_INCREMENT_CMD) { + action = MfClassicActionDataInc; + } + if(!mf_classic_is_allowed_access(emulator, block, access_key, action)) { + break; + } + + int32_t prev_value; + uint8_t addr; + if(!mf_classic_block_to_value(emulator->data.block[block].value, &prev_value, &addr)) { + break; + } + + // Send ACK + uint8_t ack = MF_CLASSIC_ACK_CMD; + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; + if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) break; + + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + int32_t value = *(int32_t*)&plain_data[0]; + if(value < 0) { + value = -value; + } + if(cmd == MF_CLASSIC_DECREMENT_CMD) { + value = -value; + } else if(cmd == MF_CLASSIC_RESTORE_CMD) { + value = 0; + } + + mf_classic_value_to_block(prev_value + value, addr, transfer_buf); + transfer_buf_valid = true; + // Commands do not ACK + tx_rx->tx_bits = 0; + } else if(cmd == MF_CLASSIC_TRANSFER_CMD) { + uint8_t block = plain_data[1]; + if(!mf_classic_is_allowed_access(emulator, block, access_key, MfClassicActionDataDec)) { + break; + } + if(memcmp(transfer_buf, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != + 0) { + memcpy(emulator->data.block[block].value, transfer_buf, MF_CLASSIC_BLOCK_SIZE); + emulator->data_changed = true; + } + transfer_buf_valid = false; + + uint8_t ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { - // Unknown command + FURI_LOG_T(TAG, "Unknown command: %02X", cmd); break; } } if(!command_processed) { // Send NACK - uint8_t nack = 0x04; + uint8_t nack = transfer_buf_valid ? MF_CLASSIC_NACK_BUF_VALID_CMD : + MF_CLASSIC_NACK_BUF_INVALID_CMD; if(is_encrypted) { crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { @@ -996,37 +1116,50 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } +void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { + furi_assert(tx_rx); + + uint8_t plain_data[4] = {0x50, 0x00, 0x00, 0x00}; + + nfca_append_crc16(plain_data, 2); + if(crypto) { + crypto1_encrypt( + crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); + } else { + memcpy(tx_rx->tx_data, plain_data, sizeof(plain_data)); + nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(plain_data)); + } + + tx_rx->tx_bits = sizeof(plain_data) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + furi_hal_nfc_tx_rx(tx_rx, 50); +} + bool mf_classic_write_block( FuriHalNfcTxRxContext* tx_rx, - MfClassicBlock* src_block, + Crypto1* crypto, uint8_t block_num, - MfClassicKey key_type, - uint64_t key) { + MfClassicBlock* src_block) { furi_assert(tx_rx); + furi_assert(crypto); furi_assert(src_block); - Crypto1 crypto = {}; - uint8_t plain_data[18] = {}; - uint8_t resp = 0; bool write_success = false; + uint8_t plain_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; + uint8_t resp; do { - furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { - FURI_LOG_D(TAG, "Auth fail"); - break; - } // Send write command plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; plain_data[1] = block_num; nfca_append_crc16(plain_data, 2); - crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); break; @@ -1044,7 +1177,7 @@ bool mf_classic_write_block( memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); crypto1_encrypt( - &crypto, + crypto, NULL, plain_data, (MF_CLASSIC_BLOCK_SIZE + 2) * 8, @@ -1054,8 +1187,8 @@ bool mf_classic_write_block( tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); - if(resp != 0x0A) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != MF_CLASSIC_ACK_CMD) { FURI_LOG_D(TAG, "NACK received on sending data"); break; } @@ -1067,22 +1200,200 @@ bool mf_classic_write_block( FURI_LOG_D(TAG, "Failed to send data"); break; } - write_success = true; - // Send Halt - plain_data[0] = 0x50; - plain_data[1] = 0x00; - nfca_append_crc16(plain_data, 2); - crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 2 * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; - // No response is expected - furi_hal_nfc_tx_rx(tx_rx, 50); + write_success = true; } while(false); return write_success; } +bool mf_classic_auth_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + + if(!mf_classic_write_block(tx_rx, &crypto, block_num, src_block)) { + FURI_LOG_D(TAG, "Write fail"); + break; + } + write_success = true; + + mf_classic_halt(tx_rx, &crypto); + } while(false); + + return write_success; +} + +bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num) { + furi_assert(tx_rx); + furi_assert(crypto); + + // Send transfer command + uint8_t plain_data[4] = {MF_CLASSIC_TRANSFER_CMD, block_num, 0, 0}; + uint8_t resp = 0; + bool transfer_success = false; + + nfca_append_crc16(plain_data, 2); + crypto1_encrypt( + crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = sizeof(plain_data) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + do { + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send transfer cmd"); + break; + } + + transfer_success = true; + } while(false); + + return transfer_success; +} + +bool mf_classic_value_cmd( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + uint8_t cmd, + int32_t d_value) { + furi_assert(tx_rx); + furi_assert(crypto); + furi_assert( + cmd == MF_CLASSIC_INCREMENT_CMD || cmd == MF_CLASSIC_DECREMENT_CMD || + cmd == MF_CLASSIC_RESTORE_CMD); + furi_assert(d_value >= 0); + + uint8_t plain_data[sizeof(d_value) + 2] = {}; + uint8_t resp = 0; + bool success = false; + + do { + // Send cmd + plain_data[0] = cmd; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, &d_value, sizeof(d_value)); + nfca_append_crc16(plain_data, sizeof(d_value)); + crypto1_encrypt( + crypto, NULL, plain_data, (sizeof(d_value) + 2) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = (sizeof(d_value) + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // inc, dec, restore do not ACK, but they do NACK + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not NACK received"); + break; + } + } + + success = true; + + } while(false); + + return success; +} + +bool mf_classic_value_cmd_full( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key, + int32_t d_value) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t cmd; + bool success = false; + + if(d_value > 0) { + cmd = MF_CLASSIC_INCREMENT_CMD; + } else if(d_value < 0) { + cmd = MF_CLASSIC_DECREMENT_CMD; + d_value = -d_value; + } else { + cmd = MF_CLASSIC_RESTORE_CMD; + } + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { + FURI_LOG_D(TAG, "Value cmd auth fail"); + break; + } + if(!mf_classic_value_cmd(tx_rx, &crypto, block_num, cmd, d_value)) { + FURI_LOG_D(TAG, "Value cmd inc/dec/res fail"); + break; + } + + if(!mf_classic_transfer(tx_rx, &crypto, block_num)) { + FURI_LOG_D(TAG, "Value cmd transfer fail"); + break; + } + + success = true; + + // Send Halt + mf_classic_halt(tx_rx, &crypto); + } while(false); + + return success; +} + bool mf_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, @@ -1103,31 +1414,99 @@ bool mf_classic_write_sector( // Compare blocks if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE) != 0) { - bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( - dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); - bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( - dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + if(mf_classic_is_value_block(dest_data, i)) { + bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataInc); + bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataInc); + bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataDec); + bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataDec); - if(key_a_found && key_a_write_allowed) { - FURI_LOG_I(TAG, "Writing block %d with key A", i); - uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { - FURI_LOG_E(TAG, "Failed to write block %d", i); - write_success = false; - break; - } - } else if(key_b_found && key_b_write_allowed) { - FURI_LOG_I(TAG, "Writing block %d with key A", i); - uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); - if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { - FURI_LOG_E(TAG, "Failed to write block %d", i); - write_success = false; - break; + int32_t src_value, dst_value; + + mf_classic_block_to_value(src_data->block[i].value, &src_value, NULL); + mf_classic_block_to_value(dest_data->block[i].value, &dst_value, NULL); + + int32_t diff = src_value - dst_value; + + if(diff > 0) { + if(key_a_found && key_a_inc_allowed) { + FURI_LOG_I(TAG, "Incrementing block %d with key A by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_inc_allowed) { + FURI_LOG_I(TAG, "Incrementing block %d with key B by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + } + } else if(diff < 0) { + if(key_a_found && key_a_dec_allowed) { + FURI_LOG_I(TAG, "Decrementing block %d with key A by %ld", i, -diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_dec_allowed) { + FURI_LOG_I(TAG, "Decrementing block %d with key B by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + } + } else { + FURI_LOG_E(TAG, "Value block %d address changed, cannot write it", i); } } else { - FURI_LOG_E(TAG, "Failed to find key with write access"); - write_success = false; - break; + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_auth_write_block( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_auth_write_block( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } } } else { FURI_LOG_D(TAG, "Blocks %d are equal", i); diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 321ab5f03..a88781f9c 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -120,6 +120,12 @@ bool mf_classic_is_allowed_access_data_block( MfClassicKey key, MfClassicAction action); +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num); + +bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr); + +void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -177,6 +183,12 @@ void mf_classic_reader_add_sector( uint64_t key_a, uint64_t key_b); +bool mf_classic_read_block( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + MfClassicBlock* block); + void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); uint8_t mf_classic_read_card( @@ -188,13 +200,38 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); +void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); + bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + MfClassicBlock* src_block); + +bool mf_classic_auth_write_block( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, MfClassicKey key_type, uint64_t key); +bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num); + +bool mf_classic_value_cmd( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + uint8_t cmd, + int32_t d_value); + +bool mf_classic_value_cmd_full( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key, + int32_t d_value); + bool mf_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, diff --git a/lib/nfc/protocols/nfc_util.c b/lib/nfc/protocols/nfc_util.c index 9de6f982b..8cb6d57f2 100644 --- a/lib/nfc/protocols/nfc_util.c +++ b/lib/nfc/protocols/nfc_util.c @@ -23,7 +23,7 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) { } } -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len) { +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) { furi_assert(src); furi_assert(len <= 8); @@ -45,3 +45,26 @@ uint8_t nfc_util_even_parity32(uint32_t data) { uint8_t nfc_util_odd_parity8(uint8_t data) { return nfc_util_odd_byte_parity[data]; } + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) { + furi_assert(src); + furi_assert(dst); + + uint8_t parity = 0; + uint8_t bit = 0; + while(len--) { + parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first + bit++; + if(bit == 8) { + *dst = parity; + dst++; + parity = 0; + bit = 0; + } + src++; + } + + if(bit) { + *dst = parity; + } +} \ No newline at end of file diff --git a/lib/nfc/protocols/nfc_util.h b/lib/nfc/protocols/nfc_util.h index d530badc3..04fa7622b 100644 --- a/lib/nfc/protocols/nfc_util.h +++ b/lib/nfc/protocols/nfc_util.h @@ -4,8 +4,10 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len); +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_odd_parity8(uint8_t data); + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len); From 904b55d6cfd0147c589867589745b34db24541c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Vesel=C3=BD?= Date: Tue, 7 Feb 2023 15:53:21 +0100 Subject: [PATCH 20/75] Fix CTRL-SHIFT in mousejacker --- applications/plugins/mousejacker/mousejacker_ducky.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c index 7a57856e1..b3b04d836 100644 --- a/applications/plugins/mousejacker/mousejacker_ducky.c +++ b/applications/plugins/mousejacker/mousejacker_ducky.c @@ -291,7 +291,7 @@ static bool mj_process_ducky_line( strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 2, dk.hid, plugin_state); + send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state); return true; } else if( strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 || From 1eda913367e3ab7a42344c42c5d8e7e0f9144d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 00:35:49 +0900 Subject: [PATCH 21/75] [FL-3075] Pin Reset (#2367) * Nfc: fix PVS warnings * Factory reset combo, initial version * Recovery screen and correct input pin initialization * Better pin and factory reset message * Down to cancel factory reset --- firmware/targets/f7/Inc/alt_boot.h | 2 + firmware/targets/f7/Src/main.c | 4 ++ firmware/targets/f7/Src/recovery.c | 54 +++++++++++++++++++ .../targets/f7/furi_hal/furi_hal_resources.c | 18 +++++-- lib/nfc/protocols/mifare_classic.c | 6 +-- 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 firmware/targets/f7/Src/recovery.c diff --git a/firmware/targets/f7/Inc/alt_boot.h b/firmware/targets/f7/Inc/alt_boot.h index 91bf9bdf7..d8be3aa48 100644 --- a/firmware/targets/f7/Inc/alt_boot.h +++ b/firmware/targets/f7/Inc/alt_boot.h @@ -8,6 +8,8 @@ void flipper_boot_update_exec(); void flipper_boot_dfu_exec(); +void flipper_boot_recovery_exec(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index d9a2221a2..1f2b5d6e4 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -49,6 +49,10 @@ int main() { // But if we do, abandon to avoid bootloops furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); furi_hal_power_reset(); + } else if(!furi_hal_gpio_read(&gpio_button_up)) { + furi_hal_light_sequence("rgb WR"); + flipper_boot_recovery_exec(); + furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); furi_thread_start(main_thread); diff --git a/firmware/targets/f7/Src/recovery.c b/firmware/targets/f7/Src/recovery.c new file mode 100644 index 000000000..fa57482ca --- /dev/null +++ b/firmware/targets/f7/Src/recovery.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include + +#define COUNTER_VALUE (100U) + +static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) { + u8g2_ClearBuffer(fb); + u8g2_SetDrawColor(fb, 0x01); + + u8g2_SetFont(fb, u8g2_font_helvB08_tr); + u8g2_DrawStr(fb, 2, 8, "PIN and Factory Reset"); + u8g2_SetFont(fb, u8g2_font_haxrcorp4089_tr); + u8g2_DrawStr(fb, 2, 21, "Hold Right to confirm"); + u8g2_DrawStr(fb, 2, 31, "Press Down to cancel"); + + if(progress < COUNTER_VALUE) { + size_t width = progress / (COUNTER_VALUE / 100); + u8g2_DrawBox(fb, 14 + (50 - width / 2), 54, width, 3); + } + + u8g2_SetPowerSave(fb, 0); + u8g2_SendBuffer(fb); +} + +void flipper_boot_recovery_exec() { + u8g2_t* fb = malloc(sizeof(u8g2_t)); + u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + u8g2_InitDisplay(fb); + + size_t counter = COUNTER_VALUE; + while(counter) { + if(!furi_hal_gpio_read(&gpio_button_down)) { + break; + } + + if(!furi_hal_gpio_read(&gpio_button_right)) { + counter--; + } else { + counter = COUNTER_VALUE; + } + + flipper_boot_recovery_draw_splash(fb, counter); + } + + if(!counter) { + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + } +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 98ebedf51..4a32d7087 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -73,8 +73,18 @@ const InputPin input_pins[] = { const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + void furi_hal_resources_init_early() { - furi_hal_gpio_init(&gpio_button_left, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control furi_hal_gpio_write(&periph_power, 1); @@ -117,14 +127,12 @@ void furi_hal_resources_init_early() { } void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); } void furi_hal_resources_init() { // Button pins - for(size_t i = 0; i < input_pins_count; i++) { - furi_hal_gpio_init( - input_pins[i].gpio, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedLow); - } + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 28667f09b..e4d5e0274 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -438,9 +438,9 @@ bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* ad void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { uint32_t v_inv = ~((uint32_t)value); - memcpy(block, &value, 4); - memcpy(block + 4, &v_inv, 4); - memcpy(block + 8, &value, 4); + memcpy(block, &value, 4); //-V1086 + memcpy(block + 4, &v_inv, 4); //-V1086 + memcpy(block + 8, &value, 4); //-V1086 block[12] = addr; block[13] = ~addr & 0xFF; From 224d0aefe41edd8356aa41aa6b39cbe7659c5d98 Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 7 Feb 2023 19:33:05 +0300 Subject: [PATCH 22/75] [FL-2733] multitarget support for fbt (#2209) * First part of multitarget porting * Delete firmware/targets/f7/Inc directory * Delete firmware/targets/f7/Src directory * gpio: cli fixes; about: using version from HAL * sdk: path fixes * gui: include fixes * applications: more include fixes * gpio: ported to new apis * hal: introduced furi_hal_target_hw.h; libs: added one_wire * hal: f18 target * github: also build f18 by default * typo fix * fbt: removed extra checks on app list * api: explicitly bundling select mlib headers with sdk * hal: f18: changed INPUT_DEBOUNCE_TICKS to match f7 * cleaned up commented out code * docs: added info on hw targets * docs: targets: formatting fixes * f18: fixed link error * f18: fixed API version to match f7 * docs: hardware: minor wording fixes * faploader: added fw target check * docs: typo fixes * github: not building komi target by default * fbt: support for `targets` field for built-in apps * github: reworked build flow to exclude app_set; fbt: removed komi-specific appset; added additional target buildset check * github: fixed build; nfc: fixed pvs warnings * attempt to fix target id * f7, f18: removed certain HAL function from public API * apps: debug: enabled bt_debug_app for f18 * Targets: backport input pins configuration routine from F7 to F18 Co-authored-by: Aleksandr Kutuzov --- .github/workflows/build.yml | 11 +- .github/workflows/pvs_studio.yml | 2 +- .vscode/extensions.json | 7 +- applications/debug/accessor/application.fam | 1 + .../bt_debug_app/views/bt_carrier_test.c | 2 +- .../debug/bt_debug_app/views/bt_packet_test.c | 2 +- .../file_browser_test/file_browser_app.c | 9 +- .../debug/lfrfid_debug/application.fam | 1 + .../main/archive/helpers/archive_browser.c | 5 +- .../main/archive/views/archive_browser_view.h | 9 +- .../scenes/bad_usb_scene_file_select.c | 4 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 2 +- applications/main/gpio/gpio_app.c | 4 +- applications/main/gpio/gpio_app_i.h | 3 +- applications/main/gpio/gpio_item.c | 51 - applications/main/gpio/gpio_item.h | 15 - applications/main/gpio/gpio_items.c | 69 + applications/main/gpio/gpio_items.h | 29 + .../main/gpio/scenes/gpio_scene_start.c | 4 +- .../main/gpio/scenes/gpio_scene_test.c | 8 +- .../gpio/scenes/gpio_scene_usb_uart_config.c | 2 +- applications/main/gpio/usb_uart_bridge.c | 8 +- applications/main/gpio/views/gpio_test.c | 30 +- applications/main/gpio/views/gpio_test.h | 4 +- applications/main/gpio/views/gpio_usb_uart.c | 2 +- applications/main/ibutton/application.fam | 1 + applications/main/infrared/application.fam | 1 + .../main/infrared/scenes/infrared_scene_rpc.c | 2 +- .../main/infrared/views/infrared_debug_view.c | 6 +- .../infrared/views/infrared_progress_view.c | 20 +- applications/main/lfrfid/application.fam | 1 + applications/main/nfc/application.fam | 1 + applications/main/nfc/nfc.c | 2 +- applications/main/subghz/application.fam | 1 + applications/main/u2f/scenes/u2f_scene_main.c | 2 +- .../music_player/music_player_worker.c | 1 + .../plugins/nfc_magic/application.fam | 1 + applications/plugins/picopass/application.fam | 1 + .../signal_generator/signal_gen_app_i.h | 4 +- .../signal_generator/views/signal_gen_pwm.c | 2 +- .../plugins/weather_station/application.fam | 1 + .../weather_station/protocols/ws_generic.h | 2 +- applications/services/cli/cli_command_gpio.c | 66 +- applications/services/dialogs/dialogs_api.c | 2 +- .../dialogs/dialogs_module_file_browser.c | 3 +- applications/services/dolphin/dolphin.h | 5 +- applications/services/gui/elements.c | 7 +- applications/services/gui/gui.c | 1 - .../services/gui/modules/button_menu.c | 13 +- .../services/gui/modules/button_panel.c | 13 +- .../services/gui/modules/byte_input.c | 7 +- .../services/gui/modules/file_browser.c | 13 +- .../gui/modules/file_browser_worker.c | 11 +- applications/services/gui/modules/loading.c | 9 +- applications/services/gui/modules/menu.c | 2 +- applications/services/gui/modules/submenu.c | 2 +- applications/services/gui/modules/text_box.c | 4 +- .../services/gui/modules/validators.c | 2 +- .../services/gui/modules/validators.h | 1 + .../services/gui/modules/variable_item_list.c | 6 +- applications/services/gui/modules/widget.c | 4 +- applications/services/gui/view_stack.c | 5 +- .../services/notification/notification_app.c | 2 +- .../notification/notification_messages.c | 2 +- applications/settings/about/about.c | 24 +- .../bt_settings_scene_forget_dev_confirm.c | 2 +- .../bt_settings_scene_forget_dev_success.c | 2 +- .../scenes/bt_settings_scene_start.c | 2 +- .../scenes/storage_move_to_sd_scene_confirm.c | 6 +- .../storage_move_to_sd/storage_move_to_sd.h | 7 +- applications/system/updater/cli/updater_cli.c | 2 +- .../system/updater/util/update_task.c | 4 +- documentation/HardwareTargets.md | 44 + firmware.scons | 57 +- firmware/SConscript | 17 +- firmware/targets/f18/api_symbols.csv | 2307 +++++++++++++++++ firmware/targets/f18/furi_hal/furi_hal.c | 91 + .../targets/f18/furi_hal/furi_hal_resources.c | 201 ++ .../targets/f18/furi_hal/furi_hal_resources.h | 116 + .../f18/furi_hal/furi_hal_spi_config.c | 377 +++ .../f18/furi_hal/furi_hal_spi_config.h | 55 + .../targets/f18/furi_hal/furi_hal_target_hw.h | 1 + .../f18/furi_hal/furi_hal_version_device.c | 21 + firmware/targets/f18/target.json | 55 + firmware/targets/f7/api_symbols.csv | 41 +- firmware/targets/f7/furi_hal/furi_hal.c | 6 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 4 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 2 +- .../targets/f7/furi_hal/furi_hal_cortex.c | 2 +- .../targets/f7/furi_hal/furi_hal_i2c_config.c | 2 +- .../furi_hal}/furi_hal_ibutton.h | 2 +- .../targets/f7/furi_hal/furi_hal_infrared.c | 2 +- .../targets/f7/furi_hal/furi_hal_interrupt.c | 4 +- firmware/targets/f7/furi_hal/furi_hal_light.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 2 +- .../furi_hal}/furi_hal_nfc.h | 0 firmware/targets/f7/furi_hal/furi_hal_pwm.c | 2 +- .../targets/f7/furi_hal/furi_hal_random.c | 2 +- .../targets/f7/furi_hal/furi_hal_resources.c | 17 + .../targets/f7/furi_hal/furi_hal_resources.h | 9 + .../furi_hal}/furi_hal_rfid.h | 0 firmware/targets/f7/furi_hal/furi_hal_sd.c | 13 +- firmware/targets/f7/furi_hal/furi_hal_spi.c | 28 +- .../targets/f7/furi_hal/furi_hal_spi_config.c | 25 + .../targets/f7/furi_hal/furi_hal_subghz.c | 4 +- .../furi_hal}/furi_hal_subghz.h | 0 .../targets/f7/furi_hal/furi_hal_target_hw.h | 6 + firmware/targets/f7/furi_hal/furi_hal_usb.c | 6 +- .../targets/f7/furi_hal/furi_hal_usb_cdc.c | 8 +- .../targets/f7/furi_hal/furi_hal_usb_hid.c | 8 +- .../targets/f7/furi_hal/furi_hal_usb_u2f.c | 8 +- .../targets/f7/furi_hal/furi_hal_version.c | 8 - .../f7/furi_hal/furi_hal_version_device.c | 21 + .../targets/f7/{Inc => inc}/FreeRTOSConfig.h | 0 firmware/targets/f7/{Inc => inc}/alt_boot.h | 0 firmware/targets/f7/{Inc => inc}/stm32.h | 0 .../targets/f7/{Inc => inc}/stm32_assert.h | 0 firmware/targets/f7/{Src => src}/dfu.c | 0 firmware/targets/f7/{Src => src}/main.c | 0 firmware/targets/f7/{Src => src}/recovery.c | 2 +- .../f7/{Src => src}/system_stm32wbxx.c | 0 firmware/targets/f7/{Src => src}/update.c | 2 +- firmware/targets/f7/stm32wb55xx_flash.ld | 2 +- firmware/targets/f7/stm32wb55xx_ram_fw.ld | 2 +- firmware/targets/f7/target.json | 45 + firmware/targets/furi_hal_include/furi_hal.h | 59 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../targets/furi_hal_include/furi_hal_spi.h | 6 +- .../furi_hal_include/furi_hal_version.h | 18 + lib/SConscript | 34 +- .../view_modules/popup_vm.cpp | 3 +- .../application_manifest.c | 7 + .../application_manifest.h | 8 + lib/flipper_application/flipper_application.c | 4 + lib/misc.scons | 1 - lib/nfc/parsers/all_in_one.c | 2 +- lib/nfc/parsers/plantain_4k_parser.c | 2 +- lib/nfc/parsers/plantain_parser.c | 2 +- lib/nfc/parsers/two_cities.c | 2 +- lib/nfc/protocols/mifare_ultralight.c | 2 +- lib/one_wire/SConscript | 27 + lib/subghz/blocks/generic.h | 4 +- lib/subghz/protocols/princeton_for_testing.c | 2 +- lib/subghz/subghz_setting.c | 2 +- lib/update_util/update_operation.c | 7 +- scripts/fbt/appmanifest.py | 74 +- scripts/fbt_tools/fbt_apps.py | 11 +- scripts/fbt_tools/fbt_hwtarget.py | 134 + scripts/fbt_tools/sconsmodular.py | 2 + site_scons/commandline.scons | 3 +- site_scons/extapps.scons | 17 +- site_scons/firmwareopts.scons | 9 +- 152 files changed, 4140 insertions(+), 495 deletions(-) delete mode 100644 applications/main/gpio/gpio_item.c delete mode 100644 applications/main/gpio/gpio_item.h create mode 100644 applications/main/gpio/gpio_items.c create mode 100644 applications/main/gpio/gpio_items.h create mode 100644 documentation/HardwareTargets.md create mode 100644 firmware/targets/f18/api_symbols.csv create mode 100644 firmware/targets/f18/furi_hal/furi_hal.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_resources.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_resources.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_spi_config.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_spi_config.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_target_hw.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_version_device.c create mode 100644 firmware/targets/f18/target.json rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_ibutton.h (98%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_nfc.h (100%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_rfid.h (100%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_subghz.h (100%) create mode 100644 firmware/targets/f7/furi_hal/furi_hal_target_hw.h create mode 100644 firmware/targets/f7/furi_hal/furi_hal_version_device.c rename firmware/targets/f7/{Inc => inc}/FreeRTOSConfig.h (100%) rename firmware/targets/f7/{Inc => inc}/alt_boot.h (100%) rename firmware/targets/f7/{Inc => inc}/stm32.h (100%) rename firmware/targets/f7/{Inc => inc}/stm32_assert.h (100%) rename firmware/targets/f7/{Src => src}/dfu.c (100%) rename firmware/targets/f7/{Src => src}/main.c (100%) rename firmware/targets/f7/{Src => src}/recovery.c (99%) rename firmware/targets/f7/{Src => src}/system_stm32wbxx.c (100%) rename firmware/targets/f7/{Src => src}/update.c (99%) create mode 100644 firmware/targets/f7/target.json create mode 100644 lib/one_wire/SConscript create mode 100644 scripts/fbt_tools/fbt_hwtarget.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f7233e46..080e7c2c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: pull_request: env: - TARGETS: f7 + TARGETS: f7 f18 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work @@ -60,8 +60,9 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ - copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET copro_dist updater_package \ + ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} done - name: 'Move upload files' @@ -186,6 +187,6 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ - updater_package DEBUG=0 COMPACT=1 + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package done diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 50f8f0aa6..a4ac6e301 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -89,6 +89,6 @@ jobs: - name: 'Raise exception' if: ${{ steps.pvs-warn.outputs.warnings != 0 }} run: | - echo "Please fix all PVS varnings before merge" + echo "Please fix all PVS warnings before merge" exit 1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b53ffc24c..b5791a91e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -11,5 +11,8 @@ "augustocdias.tasks-shell-input" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} \ No newline at end of file + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cmake-tools" + ] +} diff --git a/applications/debug/accessor/application.fam b/applications/debug/accessor/application.fam index 93fc9bc3f..6b8472711 100644 --- a/applications/debug/accessor/application.fam +++ b/applications/debug/accessor/application.fam @@ -2,6 +2,7 @@ App( appid="accessor", name="Accessor", apptype=FlipperAppType.DEBUG, + targets=["f7"], entry_point="accessor_app", cdefines=["APP_ACCESSOR"], requires=["gui"], diff --git a/applications/debug/bt_debug_app/views/bt_carrier_test.c b/applications/debug/bt_debug_app/views/bt_carrier_test.c index c09aa3fdf..8e2240495 100644 --- a/applications/debug/bt_debug_app/views/bt_carrier_test.c +++ b/applications/debug/bt_debug_app/views/bt_carrier_test.c @@ -1,7 +1,7 @@ #include "bt_carrier_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtCarrierTest { BtTest* bt_test; diff --git a/applications/debug/bt_debug_app/views/bt_packet_test.c b/applications/debug/bt_debug_app/views/bt_packet_test.c index 7cbc3c5c5..8a56a3003 100644 --- a/applications/debug/bt_debug_app/views/bt_packet_test.c +++ b/applications/debug/bt_debug_app/views/bt_packet_test.c @@ -1,7 +1,7 @@ #include "bt_packet_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtPacketTest { BtTest* bt_test; diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index bf423d34e..c3e7c898b 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -1,10 +1,11 @@ -#include #include "file_browser_app_i.h" -#include "gui/modules/file_browser.h" -#include -#include +#include + +#include #include #include +#include +#include static bool file_browser_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); diff --git a/applications/debug/lfrfid_debug/application.fam b/applications/debug/lfrfid_debug/application.fam index 6844f9291..323f77818 100644 --- a/applications/debug/lfrfid_debug/application.fam +++ b/applications/debug/lfrfid_debug/application.fam @@ -2,6 +2,7 @@ App( appid="lfrfid_debug", name="LF-RFID Debug", apptype=FlipperAppType.DEBUG, + targets=["f7"], entry_point="lfrfid_debug_app", requires=[ "gui", diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 6c8c9633a..1133f6d71 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -1,10 +1,11 @@ -#include #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include "../views/archive_browser_view.h" + #include #include -#include "gui/modules/file_browser_worker.h" +#include #include #include diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 915b5307a..0a000e5ac 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -1,14 +1,15 @@ #pragma once +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" + #include #include #include #include -#include +#include #include -#include "../helpers/archive_files.h" -#include "../helpers/archive_favorites.h" -#include "gui/modules/file_browser_worker.h" +#include #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index 9264eb976..f1f34f5bc 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -1,6 +1,6 @@ #include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" +#include +#include #include static bool bad_usb_file_select(BadUsbApp* bad_usb) { diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 689f3b4ea..1e3534822 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -1,7 +1,7 @@ #include "../bad_usb_script.h" #include "../bad_usb_app_i.h" #include "../views/bad_usb_view.h" -#include "furi_hal.h" +#include #include "toolbox/path.h" void bad_usb_scene_work_ok_callback(InputType type, void* context) { diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index b8afdc8ea..1ecff1ec2 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() { GpioApp* app = malloc(sizeof(GpioApp)); app->gui = furi_record_open(RECORD_GUI); + app->gpio_items = gpio_items_alloc(); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); @@ -47,7 +48,7 @@ GpioApp* gpio_app_alloc() { app->view_dispatcher, GpioAppViewVarItemList, variable_item_list_get_view(app->var_item_list)); - app->gpio_test = gpio_test_alloc(); + app->gpio_test = gpio_test_alloc(app->gpio_items); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); @@ -91,6 +92,7 @@ void gpio_app_free(GpioApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + gpio_items_free(app->gpio_items); free(app); } diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index 8f805891b..03fe9f489 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -1,7 +1,7 @@ #pragma once #include "gpio_app.h" -#include "gpio_item.h" +#include "gpio_items.h" #include "scenes/gpio_scene.h" #include "gpio_custom_event.h" #include "usb_uart_bridge.h" @@ -28,6 +28,7 @@ struct GpioApp { VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; + GPIOItems* gpio_items; UsbUartBridge* usb_uart_bridge; UsbUartConfig* usb_uart_cfg; }; diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c deleted file mode 100644 index 2d0f5f676..000000000 --- a/applications/main/gpio/gpio_item.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "gpio_item.h" - -#include - -typedef struct { - const char* name; - const GpioPin* pin; -} GpioItem; - -static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { - {"1.2: PA7", &gpio_ext_pa7}, - {"1.3: PA6", &gpio_ext_pa6}, - {"1.4: PA4", &gpio_ext_pa4}, - {"1.5: PB3", &gpio_ext_pb3}, - {"1.6: PB2", &gpio_ext_pb2}, - {"1.7: PC3", &gpio_ext_pc3}, - {"2.7: PC1", &gpio_ext_pc1}, - {"2.8: PC0", &gpio_ext_pc0}, -}; - -void gpio_item_configure_pin(uint8_t index, GpioMode mode) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, false); - furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); -} - -void gpio_item_configure_all_pins(GpioMode mode) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_configure_pin(i, mode); - } -} - -void gpio_item_set_pin(uint8_t index, bool level) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, level); -} - -void gpio_item_set_all_pins(bool level) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_set_pin(i, level); - } -} - -const char* gpio_item_get_pin_name(uint8_t index) { - furi_assert(index < GPIO_ITEM_COUNT + 1); - if(index == GPIO_ITEM_COUNT) { - return "ALL"; - } else { - return gpio_item[index].name; - } -} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h deleted file mode 100644 index 5cb2b86c1..000000000 --- a/applications/main/gpio/gpio_item.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#define GPIO_ITEM_COUNT 8 - -void gpio_item_configure_pin(uint8_t index, GpioMode mode); - -void gpio_item_configure_all_pins(GpioMode mode); - -void gpio_item_set_pin(uint8_t index, bool level); - -void gpio_item_set_all_pins(bool level); - -const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/main/gpio/gpio_items.c b/applications/main/gpio/gpio_items.c new file mode 100644 index 000000000..02f7d95b0 --- /dev/null +++ b/applications/main/gpio/gpio_items.c @@ -0,0 +1,69 @@ +#include "gpio_items.h" + +#include + +struct GPIOItems { + GpioPinRecord* pins; + size_t count; +}; + +GPIOItems* gpio_items_alloc() { + GPIOItems* items = malloc(sizeof(GPIOItems)); + + items->count = 0; + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + items->count++; + } + } + + items->pins = malloc(sizeof(GpioPinRecord) * items->count); + for(size_t i = 0; i < items->count; i++) { + if(!gpio_pins[i].debug) { + items->pins[i].pin = gpio_pins[i].pin; + items->pins[i].name = gpio_pins[i].name; + } + } + return items; +} + +void gpio_items_free(GPIOItems* items) { + free(items->pins); + free(items); +} + +uint8_t gpio_items_get_count(GPIOItems* items) { + return items->count; +} + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, false); + furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_configure_pin(items, i, mode); + } +} + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, level); +} + +void gpio_items_set_all_pins(GPIOItems* items, bool level) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_set_pin(items, i, level); + } +} + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) { + furi_assert(index < items->count + 1); + if(index == items->count) { + return "ALL"; + } else { + return items->pins[index].name; + } +} diff --git a/applications/main/gpio/gpio_items.h b/applications/main/gpio/gpio_items.h new file mode 100644 index 000000000..68afbe693 --- /dev/null +++ b/applications/main/gpio/gpio_items.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GPIOItems GPIOItems; + +GPIOItems* gpio_items_alloc(); + +void gpio_items_free(GPIOItems* items); + +uint8_t gpio_items_get_count(GPIOItems* items); + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode); + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode); + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level); + +void gpio_items_set_all_pins(GPIOItems* items, bool level); + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 729922949..027267793 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -1,6 +1,6 @@ #include "../gpio_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" +#include +#include #include enum GpioItem { diff --git a/applications/main/gpio/scenes/gpio_scene_test.c b/applications/main/gpio/scenes/gpio_scene_test.c index b015d8090..9940b95d4 100644 --- a/applications/main/gpio/scenes/gpio_scene_test.c +++ b/applications/main/gpio/scenes/gpio_scene_test.c @@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) { } void gpio_scene_test_on_enter(void* context) { + furi_assert(context); GpioApp* app = context; - gpio_item_configure_all_pins(GpioModeOutputPushPull); + gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull); gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest); } @@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) { } void gpio_scene_test_on_exit(void* context) { - UNUSED(context); - gpio_item_configure_all_pins(GpioModeAnalog); + furi_assert(context); + GpioApp* app = context; + gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog); } diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 776343fb0..e2ab66264 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -1,6 +1,6 @@ #include "../usb_uart_bridge.h" #include "../gpio_app_i.h" -#include "furi_hal.h" +#include typedef enum { UsbUartLineIndexVcp, diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 1a82dbdc2..9bc759dc8 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -1,10 +1,10 @@ #include "usb_uart_bridge.h" -#include "furi_hal.h" -#include #include "usb_cdc.h" -#include "cli/cli_vcp.h" +#include +#include #include -#include "cli/cli.h" +#include +#include #define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) diff --git a/applications/main/gpio/views/gpio_test.c b/applications/main/gpio/views/gpio_test.c index 69dc0f67b..c154a7275 100644 --- a/applications/main/gpio/views/gpio_test.c +++ b/applications/main/gpio/views/gpio_test.c @@ -1,5 +1,5 @@ #include "gpio_test.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include @@ -11,6 +11,7 @@ struct GpioTest { typedef struct { uint8_t pin_idx; + GPIOItems* gpio_items; } GpioTestModel; static bool gpio_test_process_left(GpioTest* gpio_test); @@ -25,7 +26,12 @@ static void gpio_test_draw_callback(Canvas* canvas, void* _model) { elements_multiline_text_aligned( canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); elements_multiline_text_aligned( - canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx)); + canvas, + 64, + 32, + AlignCenter, + AlignTop, + gpio_items_get_pin_name(model->gpio_items, model->pin_idx)); } static bool gpio_test_input_callback(InputEvent* event, void* context) { @@ -64,7 +70,7 @@ static bool gpio_test_process_right(GpioTest* gpio_test) { gpio_test->view, GpioTestModel * model, { - if(model->pin_idx < GPIO_ITEM_COUNT) { + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { model->pin_idx++; } }, @@ -80,17 +86,17 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { GpioTestModel * model, { if(event->type == InputTypePress) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, true); + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, true); } else { - gpio_item_set_all_pins(true); + gpio_items_set_all_pins(model->gpio_items, true); } consumed = true; } else if(event->type == InputTypeRelease) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, false); + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, false); } else { - gpio_item_set_all_pins(false); + gpio_items_set_all_pins(model->gpio_items, false); } consumed = true; } @@ -101,11 +107,15 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { return consumed; } -GpioTest* gpio_test_alloc() { +GpioTest* gpio_test_alloc(GPIOItems* gpio_items) { GpioTest* gpio_test = malloc(sizeof(GpioTest)); gpio_test->view = view_alloc(); view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); + + with_view_model( + gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false); + view_set_context(gpio_test->view, gpio_test); view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); view_set_input_callback(gpio_test->view, gpio_test_input_callback); diff --git a/applications/main/gpio/views/gpio_test.h b/applications/main/gpio/views/gpio_test.h index 5cbd11e82..38fcbc5fb 100644 --- a/applications/main/gpio/views/gpio_test.h +++ b/applications/main/gpio/views/gpio_test.h @@ -1,11 +1,13 @@ #pragma once +#include "../gpio_items.h" + #include typedef struct GpioTest GpioTest; typedef void (*GpioTestOkCallback)(InputType type, void* context); -GpioTest* gpio_test_alloc(); +GpioTest* gpio_test_alloc(GPIOItems* gpio_items); void gpio_test_free(GpioTest* gpio_test); diff --git a/applications/main/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c index c7406d29b..837f2e3ec 100644 --- a/applications/main/gpio/views/gpio_usb_uart.c +++ b/applications/main/gpio/views/gpio_usb_uart.c @@ -1,6 +1,6 @@ #include "../usb_uart_bridge.h" #include "../gpio_app_i.h" -#include "furi_hal.h" +#include #include struct GpioUsbUart { diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 77bb9a33c..06968bba4 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -2,6 +2,7 @@ App( appid="ibutton", name="iButton", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], requires=[ diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 9c5eaf392..e5483e9ff 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -3,6 +3,7 @@ App( name="Infrared", apptype=FlipperAppType.APP, entry_point="infrared_app", + targets=["f7"], cdefines=["APP_INFRARED"], requires=[ "gui", diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 8044e8318..04f17416d 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "gui/canvas.h" +#include typedef enum { InfraredRpcStateIdle, diff --git a/applications/main/infrared/views/infrared_debug_view.c b/applications/main/infrared/views/infrared_debug_view.c index ab2c679c4..ec0896281 100644 --- a/applications/main/infrared/views/infrared_debug_view.c +++ b/applications/main/infrared/views/infrared_debug_view.c @@ -1,11 +1,11 @@ #include "infrared_debug_view.h" -#include -#include - #include #include +#include +#include + #define INFRARED_DEBUG_TEXT_LENGTH 64 struct InfraredDebugView { diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c index 3c50f89e4..432da7ff1 100644 --- a/applications/main/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -1,13 +1,15 @@ -#include -#include "furi_hal_resources.h" -#include "assets_icons.h" -#include "gui/canvas.h" -#include "gui/view.h" -#include "input/input.h" -#include -#include #include "infrared_progress_view.h" -#include "gui/modules/button_panel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include struct InfraredProgressView { diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 150a6f3db..8fe1bac4d 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -2,6 +2,7 @@ App( appid="lfrfid", name="125 kHz RFID", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], requires=[ diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index ce21bfd27..f09127045 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -2,6 +2,7 @@ App( appid="nfc", name="NFC", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="nfc_app", cdefines=["APP_NFC"], requires=[ diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 6e6dc4dcc..4540f5d9f 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,5 +1,5 @@ #include "nfc_i.h" -#include "furi_hal_nfc.h" +#include #include bool nfc_custom_event_callback(void* context, uint32_t event) { diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 7d31ecae5..6df4b1a8a 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -2,6 +2,7 @@ App( appid="subghz", name="Sub-GHz", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="subghz_app", cdefines=["APP_SUBGHZ"], requires=[ diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index af7f1159b..251bc4d99 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -1,7 +1,7 @@ #include "../u2f_app_i.h" #include "../views/u2f_view.h" #include -#include "furi_hal.h" +#include #include "../u2f.h" #define U2F_REQUEST_TIMEOUT 500 diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 60fd33a17..ee350ee80 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -6,6 +6,7 @@ #include #include +#include #include #define TAG "MusicPlayerWorker" diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam index f09d65c90..bf42681ca 100644 --- a/applications/plugins/nfc_magic/application.fam +++ b/applications/plugins/nfc_magic/application.fam @@ -2,6 +2,7 @@ App( appid="nfc_magic", name="Nfc Magic", apptype=FlipperAppType.EXTERNAL, + targets=["f7"], entry_point="nfc_magic_app", requires=[ "storage", diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index bfbe5ed02..f2da6a9fa 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -2,6 +2,7 @@ App( appid="picopass", name="PicoPass Reader", apptype=FlipperAppType.EXTERNAL, + targets=["f7"], entry_point="picopass_app", requires=[ "storage", diff --git a/applications/plugins/signal_generator/signal_gen_app_i.h b/applications/plugins/signal_generator/signal_gen_app_i.h index 47c266475..60e4d7ed9 100644 --- a/applications/plugins/signal_generator/signal_gen_app_i.h +++ b/applications/plugins/signal_generator/signal_gen_app_i.h @@ -2,8 +2,8 @@ #include "scenes/signal_gen_scene.h" -#include "furi_hal_clock.h" -#include "furi_hal_pwm.h" +#include +#include #include #include diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/plugins/signal_generator/views/signal_gen_pwm.c index b6ba47ab0..d625ed5a9 100644 --- a/applications/plugins/signal_generator/views/signal_gen_pwm.c +++ b/applications/plugins/signal_generator/views/signal_gen_pwm.c @@ -1,5 +1,5 @@ #include "../signal_gen_app_i.h" -#include "furi_hal.h" +#include #include #include diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam index 1074fc040..769b6dd27 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/plugins/weather_station/application.fam @@ -2,6 +2,7 @@ App( appid="weather_station", name="Weather Station", apptype=FlipperAppType.PLUGIN, + targets=["f7"], entry_point="weather_station_app", cdefines=["APP_WEATHER_STATION"], requires=["gui"], diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index 8e6e061ad..47cfa74b3 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -6,7 +6,7 @@ #include #include "furi.h" -#include "furi_hal.h" +#include #include #include diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index f0d487bec..d02462734 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -5,28 +5,6 @@ #include #include -typedef struct { - const GpioPin* pin; - const char* name; - const bool debug; -} CliCommandGpio; - -const CliCommandGpio cli_command_gpio_pins[] = { - {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, - {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, - {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, - {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, - {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, - {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, - {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, - {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, - /* Dangerous pins, may damage hardware */ - {.pin = &gpio_infrared_rx, .name = "PA0", .debug = true}, - {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, - {.pin = &gpio_speaker, .name = "PB8", .debug = true}, - {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, -}; - void cli_command_gpio_print_usage() { printf("Usage:\r\n"); printf("gpio \r\n"); @@ -38,9 +16,9 @@ void cli_command_gpio_print_usage() { static bool pin_name_to_int(FuriString* pin_name, size_t* result) { bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(furi_string_equal(pin_name, cli_command_gpio_pins[i].name)) { - if(!cli_command_gpio_pins[i].debug || is_debug_mode) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(furi_string_equal(pin_name, gpio_pins[i].name)) { + if(!gpio_pins[i].debug || is_debug_mode) { *result = i; return true; } @@ -53,9 +31,9 @@ static bool pin_name_to_int(FuriString* pin_name, size_t* result) { static void gpio_print_pins(void) { printf("Wrong pin name. Available pins: "); bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!cli_command_gpio_pins[i].debug || is_debug_mode) { - printf("%s ", cli_command_gpio_pins[i].name); + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug || is_debug_mode) { + printf("%s ", gpio_pins[i].name); } } } @@ -113,7 +91,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { return; } - if(cli_command_gpio_pins[num].debug) { //-V779 + if(gpio_pins[num].debug) { //-V779 printf( "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); @@ -124,12 +102,12 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { } if(value == 1) { // output - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, false); - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeOutputPushPull); - printf("Pin %s is now an output (low)", cli_command_gpio_pins[num].name); + furi_hal_gpio_write(gpio_pins[num].pin, false); + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeOutputPushPull); + printf("Pin %s is now an output (low)", gpio_pins[num].name); } else { // input - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeInput); - printf("Pin %s is now an input", cli_command_gpio_pins[num].name); + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeInput); + printf("Pin %s is now an input", gpio_pins[num].name); } } @@ -144,15 +122,14 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { } if(LL_GPIO_MODE_INPUT != //-V779 - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an input.", cli_command_gpio_pins[num].name); + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an input.", gpio_pins[num].name); return; } - uint8_t val = !!furi_hal_gpio_read(cli_command_gpio_pins[num].pin); + uint8_t val = !!furi_hal_gpio_read(gpio_pins[num].pin); - printf("Pin %s <= %u", cli_command_gpio_pins[num].name, val); + printf("Pin %s <= %u", gpio_pins[num].name, val); } void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { @@ -174,14 +151,13 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { } if(LL_GPIO_MODE_OUTPUT != //-V779 - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an output.", cli_command_gpio_pins[num].name); + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an output.", gpio_pins[num].name); return; } // Extra check if debug pins used - if(cli_command_gpio_pins[num].debug) { + if(gpio_pins[num].debug) { printf( "Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); @@ -191,8 +167,8 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { } } - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, !!value); - printf("Pin %s => %u", cli_command_gpio_pins[num].name, !!value); + furi_hal_gpio_write(gpio_pins[num].pin, !!value); + printf("Pin %s => %u", gpio_pins[num].name, !!value); } void cli_command_gpio(Cli* cli, FuriString* args, void* context) { diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index 5e2b0683e..ca2435b9b 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,4 +1,4 @@ -#include "dialogs/dialogs_message.h" +#include "dialogs_message.h" #include "dialogs_i.h" #include #include diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c index 8d486dbaf..79d151c59 100644 --- a/applications/services/dialogs/dialogs_module_file_browser.c +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -1,6 +1,7 @@ #include "dialogs_i.h" + +#include #include -#include "gui/modules/file_browser.h" typedef struct { FuriApiLock lock; diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index 41a6a6089..8757e2a37 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -1,8 +1,9 @@ #pragma once -#include -#include "gui/view.h" #include "helpers/dolphin_deed.h" + +#include +#include #include #ifdef __cplusplus diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index e22889bf9..54c36af76 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -1,16 +1,17 @@ #include "elements.h" -#include "m-core.h" +#include #include -#include "furi_hal_resources.h" +#include #include -#include "gui/canvas.h" +#include #include #include #include #include "canvas_i.h" +#include #include #include #include diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index b0903e021..af5cf862d 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,4 +1,3 @@ -#include "gui/canvas.h" #include "gui_i.h" #include diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index 427a3e1ae..6ac786c92 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -1,12 +1,15 @@ #include "button_menu.h" -#include "gui/canvas.h" -#include "gui/elements.h" -#include "input/input.h" -#include + +#include +#include +#include + #include -#include #include +#include +#include + #define ITEM_FIRST_OFFSET 17 #define ITEM_NEXT_OFFSET 4 #define ITEM_HEIGHT 14 diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c index 8f29c6542..9b816eed0 100644 --- a/applications/services/gui/modules/button_panel.c +++ b/applications/services/gui/modules/button_panel.c @@ -1,12 +1,15 @@ #include "button_panel.h" -#include "furi_hal_resources.h" -#include "gui/canvas.h" + +#include +#include + +#include +#include +#include + #include #include #include -#include -#include -#include typedef struct { // uint16_t to support multi-screen, wide button panel diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 82de129f5..b2d21f7ae 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -1,8 +1,9 @@ -#include -#include -#include #include "byte_input.h" +#include +#include +#include + struct ByteInput { View* view; }; diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index e03a032c1..d12a00ba5 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -1,14 +1,17 @@ #include "file_browser.h" -#include "assets_icons.h" #include "file_browser_worker.h" + +#include +#include +#include + +#include +#include + #include #include #include -#include "furi_hal_resources.h" #include -#include -#include -#include "toolbox/path.h" #define LIST_ITEMS 5u #define MAX_LEN_PX 110 diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index a97a4d71a..80c4f4c43 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -1,13 +1,16 @@ #include "file_browser_worker.h" + +#include +#include + +#include #include #include -#include "storage/filesystem_api_defines.h" +#include + #include #include -#include -#include #include -#include "toolbox/path.h" #define TAG "BrowserWorker" diff --git a/applications/services/gui/modules/loading.c b/applications/services/gui/modules/loading.c index 0fa2c1286..e64aeb78f 100644 --- a/applications/services/gui/modules/loading.c +++ b/applications/services/gui/modules/loading.c @@ -1,13 +1,14 @@ -#include -#include -#include +#include "loading.h" + #include #include #include #include #include -#include "loading.h" +#include +#include +#include struct Loading { View* view; diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 6983e0108..3e3b6c2e4 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -1,9 +1,9 @@ #include "menu.h" -#include #include #include #include +#include struct Menu { View* view; diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 72626c587..00e4d68b5 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -1,8 +1,8 @@ #include "submenu.h" -#include #include #include +#include struct Submenu { View* view; diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 079a1294d..01ccdbf52 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -1,7 +1,7 @@ #include "text_box.h" -#include "gui/canvas.h" -#include +#include #include +#include #include struct TextBox { diff --git a/applications/services/gui/modules/validators.c b/applications/services/gui/modules/validators.c index 9c5d0be84..a18cb925f 100644 --- a/applications/services/gui/modules/validators.c +++ b/applications/services/gui/modules/validators.c @@ -1,6 +1,6 @@ -#include #include "validators.h" #include +#include struct ValidatorIsFile { char* app_path_folder; diff --git a/applications/services/gui/modules/validators.h b/applications/services/gui/modules/validators.h index d9200b6db..e509fd833 100644 --- a/applications/services/gui/modules/validators.h +++ b/applications/services/gui/modules/validators.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 5060985e1..cf7f64ca3 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -1,8 +1,8 @@ #include "variable_item_list.h" -#include "gui/canvas.h" -#include -#include #include +#include +#include +#include #include struct VariableItem { diff --git a/applications/services/gui/modules/widget.c b/applications/services/gui/modules/widget.c index 6153b425b..21b8e5616 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -1,7 +1,7 @@ -#include #include "widget.h" -#include #include "widget_elements/widget_element_i.h" +#include +#include ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); diff --git a/applications/services/gui/view_stack.c b/applications/services/gui/view_stack.c index 3bb00fbf2..0a106f1ba 100644 --- a/applications/services/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -1,8 +1,9 @@ -#include "gui/view.h" -#include #include "view_stack.h" #include "view_i.h" +#include +#include + #define MAX_VIEWS 3 typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index b6579f547..2e170f547 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -1,4 +1,4 @@ -#include "furi_hal_light.h" +#include #include #include #include diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index d795c55d9..51f3533c3 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -1,4 +1,4 @@ -#include "furi_hal_resources.h" +#include #include "notification.h" #include "notification_messages_notes.h" #include diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 560a683cf..61c724966 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -13,17 +13,29 @@ typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMess static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - const char* screen_header = "Product: Flipper Zero\n" - "Model: FZ.1\n"; - const char* screen_text = "FCC ID: 2A2V6-FZ\n" - "IC: 27624-FZ"; + FuriString* screen_header = furi_string_alloc_printf( + "Product: %s\n" + "Model: %s", + furi_hal_version_get_model_name(), + furi_hal_version_get_model_code()); - dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop); + FuriString* screen_text = furi_string_alloc_printf( + "FCC ID: %s\n" + "IC: %s", + furi_hal_version_get_fcc_id(), + furi_hal_version_get_ic_id()); + + dialog_message_set_header( + message, furi_string_get_cstr(screen_header), 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + furi_string_free(screen_header); + furi_string_free(screen_text); + return result; } diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 964736b66..31921b9f3 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index c0b0c80a9..481ba6d5c 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { BtSettingsApp* app = context; diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index bcbc902b2..5db98e9de 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include enum BtSetting { BtSettingOff, diff --git a/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c index 71ce5a7c3..08c9e2d7f 100644 --- a/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c +++ b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c @@ -1,7 +1,7 @@ #include "../storage_move_to_sd.h" -#include "gui/canvas.h" -#include "gui/modules/widget_elements/widget_element_i.h" -#include "storage/storage.h" +#include +#include +#include static void storage_move_to_sd_scene_confirm_widget_callback( GuiButtonType result, diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.h b/applications/system/storage_move_to_sd/storage_move_to_sd.h index a62d87c1f..135f3e9b0 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.h +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.h @@ -1,17 +1,16 @@ #pragma once -#include "gui/modules/widget_elements/widget_element_i.h" -#include #include #include #include #include -#include - #include #include +#include +#include #include #include +#include #include "scenes/storage_move_to_sd_scene.h" diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index f8e21ca3e..2bf6dab26 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -76,8 +76,8 @@ static void updater_cli_ep(Cli* cli, FuriString* args, void* context) { for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) { const CliSubcommand* subcmd_def = &update_cli_subcommands[idx]; if(furi_string_cmp_str(subcommand, subcmd_def->command) == 0) { - furi_string_free(subcommand); subcmd_def->handler(args); + furi_string_free(subcommand); return; } } diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index b43a6df16..54fe27995 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -287,7 +287,9 @@ bool update_task_parse_manifest(UpdateTask* update_task) { } update_task_set_progress(update_task, UpdateTaskStageProgress, 50); - if(manifest->target != furi_hal_version_get_hw_target()) { + /* Check target only if it's set - skipped for pre-production samples */ + if(furi_hal_version_get_hw_target() && + (manifest->target != furi_hal_version_get_hw_target())) { break; } diff --git a/documentation/HardwareTargets.md b/documentation/HardwareTargets.md new file mode 100644 index 000000000..0c3474839 --- /dev/null +++ b/documentation/HardwareTargets.md @@ -0,0 +1,44 @@ +## What a Firmware Target is + +Flipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_. + +Target-specific files are placed in a single sub-folder in `firmware/targets`. It must contain a target definition file, `target.json`, and may contain other files if they are referenced by current target's definition. By default, `fbt` gathers all source files in target folder, unless they are explicitly excluded. + +Targets can inherit most code parts from other targets, to reduce common code duplication. + + +## Target Definition File + +A target definition file, `target.json`, is a JSON file that can contain the following fields: + +* `include_paths`: list of strings, folder paths relative to current target folder to add to global C/C++ header path lookup list. +* `sdk_header_paths`: list of strings, folder paths relative to current target folder to gather headers from for including in SDK. +* `startup_script`: filename of a startup script, performing initial hardware initialization. +* `linker_script_flash`: filename of a linker script for creating the main firmware image. +* `linker_script_ram`: filename of a linker script to use in "updater" build configuration. +* `linker_script_app`: filename of a linker script to use for linking .fap files. +* `sdk_symbols`: filename of a .csv file containing current SDK configuration for this target. +* `linker_dependencies`: list of libraries to link the firmware with. Note that those not in the list won't be built by `fbt`. Also several link passes might be needed, in such case you may need to specify same library name twice. +* `inherit`: string, specifies hardware target to borrow main configuration from. Current configuration may specify additional values for parameters that are lists of strings, or override values that are not lists. +* `excluded_sources`: list of filenames from the inherited configuration(s) NOT to be built. +* `excluded_headers`: list of headers from the inherited configuration(s) NOT to be included in generated SDK. +* `excluded_modules`: list of strings specifying fbt library (module) names to exclude from being used to configure build environment. + + +## Applications & Hardware + +Not all applications are available on different hardware targets. + +* For applications built into the firmware, you have to specify a compatible application set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md#firmware-application-set) for details on build configurations. + +* For applications built as external .faps, you have to explicitly specify compatible targets in application's manifest, `application.fam`. For example, to limit application to a single target, add `targets=["f7"],` to the manifest. It won't be built for other targets. + +For details on application manifests, check out [their docs page](./AppManifests.md). + + +## Building Firmware for a Specific Target + +You have to specify TARGET_HW (and, optionally, FIRMWARE_APP_SET) for `fbt` to build firmware for non-default target. For example, building and flashing debug firmware for f18 can be done with + + ./fbt TARGET_HW=18 flash_usb_full + diff --git a/firmware.scons b/firmware.scons index 3922c136e..92f2d1a91 100644 --- a/firmware.scons +++ b/firmware.scons @@ -16,6 +16,7 @@ env = ENV.Clone( "fwbin", "fbt_apps", "pvsstudio", + "fbt_hwtarget", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], @@ -31,10 +32,6 @@ env = ENV.Clone( CPPPATH=[ "#/furi", *(f"#/{app_dir[0]}" for app_dir in ENV["APPDIRS"] if app_dir[1]), - "#/firmware/targets/f${TARGET_HW}/ble_glue", - "#/firmware/targets/f${TARGET_HW}/fatfs", - "#/firmware/targets/f${TARGET_HW}/furi_hal", - "#/firmware/targets/f${TARGET_HW}/Inc", "#/firmware/targets/furi_hal_include", ], # Specific flags for building libraries - always do optimized builds @@ -74,20 +71,6 @@ env = ENV.Clone( _APP_ICONS=None, ) - -def ApplyLibFlags(env): - flags_to_apply = env["FW_LIB_OPTS"].get( - env.get("FW_LIB_NAME"), - env["FW_LIB_OPTS"]["Default"], - ) - # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) - env.MergeFlags(flags_to_apply) - - -env.AddMethod(ApplyLibFlags) - -Export("env") - if env["IS_BASE_FIRMWARE"]: env.Append( FIRMWARE_BUILD_CFG="firmware", @@ -102,7 +85,11 @@ else: ], ) -# Invoke child SConscripts to populate global `env` + build their own part of the code +env.ConfigureForTarget(env.subst("${TARGET_HW}")) + +Export("env") + +# Invoke child SCopscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", @@ -131,7 +118,7 @@ if extra_int_apps := GetOption("extra_int_apps"): fwenv.Append(APPS=extra_int_apps.split(",")) -for app_dir, _ in env["APPDIRS"]: +for app_dir, _ in fwenv["APPDIRS"]: app_dir_node = env.Dir("#").Dir(app_dir) for entry in app_dir_node.glob("*"): @@ -196,37 +183,11 @@ sources.extend( fwelf = fwenv["FW_ELF"] = fwenv.Program( "${FIRMWARE_BUILD_CFG}", sources, - LIBS=[ - "print", - "flipper${TARGET_HW}", - "furi", - "freertos", - "stm32cubewb", - "hwdrivers", - "fatfs", - "littlefs", - "subghz", - "flipperformat", - "toolbox", - "nfc", - "microtar", - "usb_stm32", - "st25rfal002", - "infrared", - "appframe", - "assets", - "misc", - "mbedtls", - "lfrfid", - "flipper_application", - # 2nd round - "flipperformat", - "toolbox", - ], + LIBS=fwenv["TARGET_CFG"].linker_dependencies, ) # Firmware depends on everything child builders returned -Depends(fwelf, lib_targets) +# Depends(fwelf, lib_targets) # Output extra details after building firmware AddPostAction(fwelf, fwenv["APPBUILD_DUMP"]) AddPostAction( diff --git a/firmware/SConscript b/firmware/SConscript index a16f14e65..fa96b0adf 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -2,15 +2,6 @@ Import("env") env.Append( LINT_SOURCES=[Dir(".")], - SDK_HEADERS=[ - *env.GlobRecursive("*.h", "targets/furi_hal_include", "*_i.h"), - *env.GlobRecursive("*.h", "targets/f${TARGET_HW}/furi_hal", "*_i.h"), - File("targets/f7/platform_specific/intrinsic_export.h"), - ], -) - -env.SetDefault( - SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv").srcnode() ) libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") @@ -22,9 +13,9 @@ libenv.Append( libenv.ApplyLibFlags() -sources = ["targets/f${TARGET_HW}/startup_stm32wb55xx_cm4.s"] -sources += libenv.GlobRecursive("*.c") - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +lib = libenv.StaticLibrary( + "${FW_LIB_NAME}", + env.get("TARGET_CFG").gatherSources(), +) libenv.Install("${LIB_DIST_DIR}", lib) Return("lib") diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv new file mode 100644 index 000000000..e4a7dfdd5 --- /dev/null +++ b/firmware/targets/f18/api_symbols.csv @@ -0,0 +1,2307 @@ +entry,status,name,type,params +Version,+,12.1,, +Header,+,applications/services/bt/bt_service/bt.h,, +Header,+,applications/services/cli/cli.h,, +Header,+,applications/services/cli/cli_vcp.h,, +Header,+,applications/services/dialogs/dialogs.h,, +Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/gui/elements.h,, +Header,+,applications/services/gui/gui.h,, +Header,+,applications/services/gui/icon_i.h,, +Header,+,applications/services/gui/modules/button_menu.h,, +Header,+,applications/services/gui/modules/button_panel.h,, +Header,+,applications/services/gui/modules/byte_input.h,, +Header,+,applications/services/gui/modules/dialog_ex.h,, +Header,+,applications/services/gui/modules/empty_screen.h,, +Header,+,applications/services/gui/modules/file_browser.h,, +Header,+,applications/services/gui/modules/file_browser_worker.h,, +Header,+,applications/services/gui/modules/loading.h,, +Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/popup.h,, +Header,+,applications/services/gui/modules/submenu.h,, +Header,+,applications/services/gui/modules/text_box.h,, +Header,+,applications/services/gui/modules/text_input.h,, +Header,+,applications/services/gui/modules/validators.h,, +Header,+,applications/services/gui/modules/variable_item_list.h,, +Header,+,applications/services/gui/modules/widget.h,, +Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, +Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_stack.h,, +Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, +Header,+,applications/services/notification/notification.h,, +Header,+,applications/services/notification/notification_messages.h,, +Header,+,applications/services/power/power_service/power.h,, +Header,+,applications/services/rpc/rpc_app.h,, +Header,+,applications/services/storage/storage.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_resources.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_spi_config.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_target_hw.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_i2c.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_info.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_light.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_memory.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_mpu.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_power.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_random.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_region.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_rtc.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_adc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_bus.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_comp.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_cortex.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_crc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_crs.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_dma.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_dmamux.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_exti.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_gpio.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_hsem.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_i2c.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_ipcc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_iwdg.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_lptim.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_lpuart.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_pka.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_pwr.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rcc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rng.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rtc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_spi.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_system.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_tim.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_usart.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_utils.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_format/flipper_format.h,, +Header,+,lib/flipper_format/flipper_format_i.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_device.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_game.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_led.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_power.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,-,lib/libusb_stm32/inc/stm32_compat.h,, +Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_cdc.h,, +Header,+,lib/libusb_stm32/inc/usb_cdca.h,, +Header,+,lib/libusb_stm32/inc/usb_cdce.h,, +Header,+,lib/libusb_stm32/inc/usb_cdci.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcp.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcw.h,, +Header,+,lib/libusb_stm32/inc/usb_dfu.h,, +Header,+,lib/libusb_stm32/inc/usb_hid.h,, +Header,+,lib/libusb_stm32/inc/usb_std.h,, +Header,+,lib/libusb_stm32/inc/usb_tmc.h,, +Header,+,lib/libusb_stm32/inc/usbd_core.h,, +Header,+,lib/mbedtls/include/mbedtls/des.h,, +Header,+,lib/mbedtls/include/mbedtls/sha1.h,, +Header,+,lib/micro-ecc/uECC.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, +Header,+,lib/print/wrappers.h,, +Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/crc32_calc.h,, +Header,+,lib/toolbox/dir_walk.h,, +Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hmac_sha256.h,, +Header,+,lib/toolbox/manchester_decoder.h,, +Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/md5.h,, +Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/random_name.h,, +Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/stream/buffered_file_stream.h,, +Header,+,lib/toolbox/stream/file_stream.h,, +Header,+,lib/toolbox/stream/stream.h,, +Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/tar/tar_archive.h,, +Header,+,lib/toolbox/value_index.h,, +Header,+,lib/toolbox/version.h,, +Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* +Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* +Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* +Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" +Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* +Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* +Function,-,LL_CRS_DeInit,ErrorStatus, +Function,+,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t" +Function,+,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*" +Function,-,LL_DMA_StructInit,void,LL_DMA_InitTypeDef* +Function,-,LL_EXTI_DeInit,ErrorStatus, +Function,-,LL_EXTI_Init,ErrorStatus,LL_EXTI_InitTypeDef* +Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* +Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* +Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" +Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" +Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* +Function,-,LL_Init1msTick,void,uint32_t +Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* +Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" +Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* +Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* +Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* +Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" +Function,-,LL_PKA_StructInit,void,LL_PKA_InitTypeDef* +Function,-,LL_PLL_ConfigSystemClock_HSE,ErrorStatus,"uint32_t, LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_HSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_MSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PWR_DeInit,ErrorStatus, +Function,-,LL_RCC_DeInit,ErrorStatus, +Function,-,LL_RCC_GetADCClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetCLK48ClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetI2CClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetLPTIMClockFreq,uint32_t,uint32_t +Function,+,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRFWKPClockFreq,uint32_t, +Function,-,LL_RCC_GetRNGClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRTCClockFreq,uint32_t, +Function,-,LL_RCC_GetSAIClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetSMPSClockFreq,uint32_t, +Function,-,LL_RCC_GetSystemClocksFreq,void,LL_RCC_ClocksTypeDef* +Function,+,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetUSBClockFreq,uint32_t,uint32_t +Function,-,LL_RNG_DeInit,ErrorStatus,RNG_TypeDef* +Function,-,LL_RNG_Init,ErrorStatus,"RNG_TypeDef*, LL_RNG_InitTypeDef*" +Function,-,LL_RNG_StructInit,void,LL_RNG_InitTypeDef* +Function,-,LL_RTC_ALMA_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMA_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_ALMB_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMB_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_DATE_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*" +Function,-,LL_RTC_DATE_StructInit,void,LL_RTC_DateTypeDef* +Function,-,LL_RTC_DeInit,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef* +Function,-,LL_RTC_ExitInitMode,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*" +Function,-,LL_RTC_StructInit,void,LL_RTC_InitTypeDef* +Function,-,LL_RTC_TIME_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*" +Function,-,LL_RTC_TIME_StructInit,void,LL_RTC_TimeTypeDef* +Function,-,LL_RTC_WaitForSynchro,ErrorStatus,RTC_TypeDef* +Function,-,LL_SPI_DeInit,ErrorStatus,SPI_TypeDef* +Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" +Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* +Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t +Function,+,LL_SetSystemCoreClock,void,uint32_t +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* +Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" +Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* +Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* +Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" +Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* +Function,-,LL_mDelay,void,uint32_t +Function,-,SystemCoreClockUpdate,void, +Function,-,SystemInit,void, +Function,-,_Exit,void,int +Function,-,__assert,void,"const char*, int, const char*" +Function,+,__assert_func,void,"const char*, int, const char*, const char*" +Function,+,__clear_cache,void,"void*, void*" +Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" +Function,+,__errno,int*, +Function,+,__furi_crash,void, +Function,+,__furi_halt,void, +Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" +Function,-,__getline,ssize_t,"char**, size_t*, FILE*" +Function,-,__itoa,char*,"int, char*, int" +Function,-,__locale_mb_cur_max,int, +Function,+,__retarget_lock_acquire,void,_LOCK_T +Function,+,__retarget_lock_acquire_recursive,void,_LOCK_T +Function,-,__retarget_lock_close,void,_LOCK_T +Function,+,__retarget_lock_close_recursive,void,_LOCK_T +Function,-,__retarget_lock_init,void,_LOCK_T* +Function,+,__retarget_lock_init_recursive,void,_LOCK_T* +Function,+,__retarget_lock_release,void,_LOCK_T +Function,+,__retarget_lock_release_recursive,void,_LOCK_T +Function,-,__retarget_lock_try_acquire,int,_LOCK_T +Function,-,__retarget_lock_try_acquire_recursive,int,_LOCK_T +Function,-,__srget_r,int,"_reent*, FILE*" +Function,-,__swbuf_r,int,"_reent*, int, FILE*" +Function,-,__utoa,char*,"unsigned, char*, int" +Function,+,__wrap___assert,void,"const char*, int, const char*" +Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" +Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_printf,int,"const char*, ..." +Function,+,__wrap_putc,int,"int, FILE*" +Function,+,__wrap_putchar,int,int +Function,+,__wrap_puts,int,const char* +Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" +Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asnprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_atoi_r,int,"_reent*, const char*" +Function,-,_atol_r,long,"_reent*, const char*" +Function,-,_atoll_r,long long,"_reent*, const char*" +Function,-,_calloc_r,void*,"_reent*, size_t, size_t" +Function,-,_diprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_dprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_drand48_r,double,_reent* +Function,-,_dtoa_r,char*,"_reent*, double, int, int, int*, int*, char**" +Function,-,_erand48_r,double,"_reent*, unsigned short[3]" +Function,-,_fclose_r,int,"_reent*, FILE*" +Function,-,_fcloseall_r,int,_reent* +Function,-,_fdopen_r,FILE*,"_reent*, int, const char*" +Function,-,_fflush_r,int,"_reent*, FILE*" +Function,-,_fgetc_r,int,"_reent*, FILE*" +Function,-,_fgetc_unlocked_r,int,"_reent*, FILE*" +Function,-,_fgetpos_r,int,"_reent*, FILE*, fpos_t*" +Function,-,_fgets_r,char*,"_reent*, char*, int, FILE*" +Function,-,_fgets_unlocked_r,char*,"_reent*, char*, int, FILE*" +Function,-,_findenv,char*,"const char*, int*" +Function,-,_findenv_r,char*,"_reent*, const char*, int*" +Function,-,_fiprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fiscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fmemopen_r,FILE*,"_reent*, void*, size_t, const char*" +Function,-,_fopen_r,FILE*,"_reent*, const char*, const char*" +Function,-,_fopencookie_r,FILE*,"_reent*, void*, const char*, cookie_io_functions_t" +Function,-,_fprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fpurge_r,int,"_reent*, FILE*" +Function,-,_fputc_r,int,"_reent*, int, FILE*" +Function,-,_fputc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_fputs_r,int,"_reent*, const char*, FILE*" +Function,-,_fputs_unlocked_r,int,"_reent*, const char*, FILE*" +Function,-,_fread_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_fread_unlocked_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_free_r,void,"_reent*, void*" +Function,-,_freopen_r,FILE*,"_reent*, const char*, const char*, FILE*" +Function,-,_fscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fseek_r,int,"_reent*, FILE*, long, int" +Function,-,_fseeko_r,int,"_reent*, FILE*, _off_t, int" +Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" +Function,-,_ftell_r,long,"_reent*, FILE*" +Function,-,_ftello_r,_off_t,"_reent*, FILE*" +Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_getc_r,int,"_reent*, FILE*" +Function,-,_getc_unlocked_r,int,"_reent*, FILE*" +Function,-,_getchar_r,int,_reent* +Function,-,_getchar_unlocked_r,int,_reent* +Function,-,_getenv_r,char*,"_reent*, const char*" +Function,-,_gets_r,char*,"_reent*, char*" +Function,-,_iprintf_r,int,"_reent*, const char*, ..." +Function,-,_iscanf_r,int,"_reent*, const char*, ..." +Function,-,_jrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_l64a_r,char*,"_reent*, long" +Function,-,_lcong48_r,void,"_reent*, unsigned short[7]" +Function,-,_lrand48_r,long,_reent* +Function,-,_malloc_r,void*,"_reent*, size_t" +Function,-,_mblen_r,int,"_reent*, const char*, size_t, _mbstate_t*" +Function,-,_mbstowcs_r,size_t,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mbtowc_r,int,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mkdtemp_r,char*,"_reent*, char*" +Function,-,_mkostemp_r,int,"_reent*, char*, int" +Function,-,_mkostemps_r,int,"_reent*, char*, int, int" +Function,-,_mkstemp_r,int,"_reent*, char*" +Function,-,_mkstemps_r,int,"_reent*, char*, int" +Function,-,_mktemp_r,char*,"_reent*, char*" +Function,-,_mrand48_r,long,_reent* +Function,-,_mstats_r,void,"_reent*, char*" +Function,-,_nrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_open_memstream_r,FILE*,"_reent*, char**, size_t*" +Function,-,_perror_r,void,"_reent*, const char*" +Function,-,_printf_r,int,"_reent*, const char*, ..." +Function,-,_putc_r,int,"_reent*, int, FILE*" +Function,-,_putc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_putchar,void,char +Function,-,_putchar_r,int,"_reent*, int" +Function,-,_putchar_unlocked_r,int,"_reent*, int" +Function,-,_putenv_r,int,"_reent*, char*" +Function,-,_puts_r,int,"_reent*, const char*" +Function,-,_realloc_r,void*,"_reent*, void*, size_t" +Function,-,_reallocf_r,void*,"_reent*, void*, size_t" +Function,-,_reclaim_reent,void,_reent* +Function,-,_remove_r,int,"_reent*, const char*" +Function,-,_rename_r,int,"_reent*, const char*, const char*" +Function,-,_rewind_r,void,"_reent*, FILE*" +Function,-,_scanf_r,int,"_reent*, const char*, ..." +Function,-,_seed48_r,unsigned short*,"_reent*, unsigned short[3]" +Function,-,_setenv_r,int,"_reent*, const char*, const char*, int" +Function,-,_siprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_siscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_sniprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_snprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_sprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_srand48_r,void,"_reent*, long" +Function,-,_sscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_strdup_r,char*,"_reent*, const char*" +Function,-,_strerror_r,char*,"_reent*, int, int, int*" +Function,-,_strndup_r,char*,"_reent*, const char*, size_t" +Function,-,_strtod_r,double,"_reent*, const char*, char**" +Function,-,_strtol_r,long,"_reent*, const char*, char**, int" +Function,-,_strtold_r,long double,"_reent*, const char*, char**" +Function,-,_strtoll_r,long long,"_reent*, const char*, char**, int" +Function,-,_strtoul_r,unsigned long,"_reent*, const char*, char**, int" +Function,-,_strtoull_r,unsigned long long,"_reent*, const char*, char**, int" +Function,-,_system_r,int,"_reent*, const char*" +Function,-,_tempnam_r,char*,"_reent*, const char*, const char*" +Function,-,_tmpfile_r,FILE*,_reent* +Function,-,_tmpnam_r,char*,"_reent*, char*" +Function,-,_tzset_r,void,_reent* +Function,-,_ungetc_r,int,"_reent*, int, FILE*" +Function,-,_unsetenv_r,int,"_reent*, const char*" +Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vasniprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasnprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vdiprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vdprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vfiprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfiscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_viprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_viscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vsiprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsiscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_vsniprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsnprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_wcstombs_r,size_t,"_reent*, char*, const wchar_t*, size_t, _mbstate_t*" +Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" +Function,-,a64l,long,const char* +Function,+,abort,void, +Function,-,abs,int,int +Function,+,acquire_mutex,void*,"ValueMutex*, uint32_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" +Function,-,arc4random,__uint32_t, +Function,-,arc4random_buf,void,"void*, size_t" +Function,-,arc4random_uniform,__uint32_t,__uint32_t +Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*" +Function,+,args_get_first_word_length,size_t,FuriString* +Function,+,args_length,size_t,FuriString* +Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" +Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" +Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,-,asctime,char*,const tm* +Function,-,asctime_r,char*,"const tm*, char*" +Function,-,asiprintf,int,"char**, const char*, ..." +Function,-,asniprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asnprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asprintf,int,"char**, const char*, ..." +Function,-,at_quick_exit,int,void (*)() +Function,-,atexit,int,void (*)() +Function,-,atof,double,const char* +Function,-,atoff,float,const char* +Function,+,atoi,int,const char* +Function,-,atol,long,const char* +Function,-,atoll,long long,const char* +Function,-,basename,char*,const char* +Function,-,bcmp,int,"const void*, const void*, size_t" +Function,-,bcopy,void,"const void*, void*, size_t" +Function,+,ble_app_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,ble_app_init,_Bool, +Function,+,ble_app_thread_stop,void, +Function,+,ble_glue_force_c2_mode,BleGlueCommandResult,BleGlueC2Mode +Function,-,ble_glue_fus_get_status,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_delete,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_install,BleGlueCommandResult,"uint32_t, uint32_t" +Function,-,ble_glue_fus_wait_operation,BleGlueCommandResult, +Function,+,ble_glue_get_c2_info,const BleGlueC2Info*, +Function,-,ble_glue_get_c2_status,BleGlueStatus, +Function,+,ble_glue_init,void, +Function,+,ble_glue_is_alive,_Bool, +Function,+,ble_glue_is_radio_stack_ready,_Bool, +Function,+,ble_glue_reinit_c2,_Bool, +Function,+,ble_glue_set_key_storage_changed_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,ble_glue_start,_Bool, +Function,+,ble_glue_thread_stop,void, +Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t +Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disconnect,void,Bt* +Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" +Function,+,buffered_file_stream_alloc,Stream*,Storage* +Function,+,buffered_file_stream_close,_Bool,Stream* +Function,+,buffered_file_stream_get_error,FS_Error,Stream* +Function,+,buffered_file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,buffered_file_stream_sync,_Bool,Stream* +Function,+,button_menu_add_item,ButtonMenuItem*,"ButtonMenu*, const char*, int32_t, ButtonMenuItemCallback, ButtonMenuItemType, void*" +Function,+,button_menu_alloc,ButtonMenu*, +Function,+,button_menu_free,void,ButtonMenu* +Function,+,button_menu_get_view,View*,ButtonMenu* +Function,+,button_menu_reset,void,ButtonMenu* +Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" +Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" +Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" +Function,+,button_panel_alloc,ButtonPanel*, +Function,+,button_panel_free,void,ButtonPanel* +Function,+,button_panel_get_view,View*,ButtonPanel* +Function,+,button_panel_reserve,void,"ButtonPanel*, size_t, size_t" +Function,+,button_panel_reset,void,ButtonPanel* +Function,+,byte_input_alloc,ByteInput*, +Function,+,byte_input_free,void,ByteInput* +Function,+,byte_input_get_view,View*,ByteInput* +Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +Function,-,bzero,void,"void*, size_t" +Function,-,calloc,void*,"size_t, size_t" +Function,+,canvas_clear,void,Canvas* +Function,+,canvas_commit,void,Canvas* +Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" +Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_disc,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_dot,void,"Canvas*, uint8_t, uint8_t" +Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" +Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" +Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" +Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" +Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" +Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_invert_color,void,Canvas* +Function,+,canvas_reset,void,Canvas* +Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" +Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_font,void,"Canvas*, Font" +Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" +Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" +Function,+,canvas_width,uint8_t,Canvas* +Function,-,cfree,void,void* +Function,-,clearerr,void,FILE* +Function,-,clearerr_unlocked,void,FILE* +Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" +Function,+,cli_cmd_interrupt_received,_Bool,Cli* +Function,+,cli_delete_command,void,"Cli*, const char*" +Function,+,cli_getc,char,Cli* +Function,+,cli_is_connected,_Bool,Cli* +Function,+,cli_nl,void, +Function,+,cli_print_usage,void,"const char*, const char*, const char*" +Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" +Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" +Function,+,cli_session_close,void,Cli* +Function,+,cli_session_open,void,"Cli*, void*" +Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,-,clock,clock_t, +Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" +Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" +Function,-,ctermid,char*,char* +Function,-,ctime,char*,const time_t* +Function,-,ctime_r,char*,"const time_t*, char*" +Function,-,cuserid,char*,char* +Function,+,delete_mutex,_Bool,ValueMutex* +Function,+,dialog_ex_alloc,DialogEx*, +Function,+,dialog_ex_disable_extended_events,void,DialogEx* +Function,+,dialog_ex_enable_extended_events,void,DialogEx* +Function,+,dialog_ex_free,void,DialogEx* +Function,+,dialog_ex_get_view,View*,DialogEx* +Function,+,dialog_ex_reset,void,DialogEx* +Function,+,dialog_ex_set_center_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_context,void,"DialogEx*, void*" +Function,+,dialog_ex_set_header,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_ex_set_icon,void,"DialogEx*, uint8_t, uint8_t, const Icon*" +Function,+,dialog_ex_set_left_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_result_callback,void,"DialogEx*, DialogExResultCallback" +Function,+,dialog_ex_set_right_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_text,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_file_browser_set_basic_options,void,"DialogsFileBrowserOptions*, const char*, const Icon*" +Function,+,dialog_file_browser_show,_Bool,"DialogsApp*, FuriString*, FuriString*, const DialogsFileBrowserOptions*" +Function,+,dialog_message_alloc,DialogMessage*, +Function,+,dialog_message_free,void,DialogMessage* +Function,+,dialog_message_set_buttons,void,"DialogMessage*, const char*, const char*, const char*" +Function,+,dialog_message_set_header,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, uint8_t" +Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" +Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" +Function,-,difftime,double,"time_t, time_t" +Function,-,diprintf,int,"int, const char*, ..." +Function,+,dir_walk_alloc,DirWalk*,Storage* +Function,+,dir_walk_close,void,DirWalk* +Function,+,dir_walk_free,void,DirWalk* +Function,+,dir_walk_get_error,FS_Error,DirWalk* +Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" +Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" +Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" +Function,-,div,div_t,"int, int" +Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed +Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp +Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed +Function,+,dolphin_flush,void,Dolphin* +Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_stats,DolphinStats,Dolphin* +Function,+,dolphin_upgrade_level,void,Dolphin* +Function,-,dprintf,int,"int, const char*, ..." +Function,-,drand48,double, +Function,-,eTaskConfirmSleepModeStatus,eSleepModeStatus, +Function,-,eTaskGetState,eTaskState,TaskHandle_t +Function,+,elements_bold_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble_str,void,"Canvas*, uint8_t, uint8_t, const char*, Align, Align" +Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_left,void,"Canvas*, const char*" +Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" +Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" +Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" +Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" +Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" +Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,empty_screen_alloc,EmptyScreen*, +Function,+,empty_screen_free,void,EmptyScreen* +Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,-,erand48,double,unsigned short[3] +Function,-,exit,void,int +Function,-,explicit_bzero,void,"void*, size_t" +Function,-,fclose,int,FILE* +Function,-,fcloseall,int, +Function,-,fdopen,FILE*,"int, const char*" +Function,-,feof,int,FILE* +Function,-,feof_unlocked,int,FILE* +Function,-,ferror,int,FILE* +Function,-,ferror_unlocked,int,FILE* +Function,-,fflush,int,FILE* +Function,-,fflush_unlocked,int,FILE* +Function,-,ffs,int,int +Function,-,ffsl,int,long +Function,-,ffsll,int,long long +Function,-,fgetc,int,FILE* +Function,-,fgetc_unlocked,int,FILE* +Function,-,fgetpos,int,"FILE*, fpos_t*" +Function,-,fgets,char*,"char*, int, FILE*" +Function,-,fgets_unlocked,char*,"char*, int, FILE*" +Function,+,file_browser_alloc,FileBrowser*,FuriString* +Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool" +Function,+,file_browser_free,void,FileBrowser* +Function,+,file_browser_get_view,View*,FileBrowser* +Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" +Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" +Function,+,file_browser_start,void,"FileBrowser*, FuriString*" +Function,+,file_browser_stop,void,FileBrowser* +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" +Function,+,file_browser_worker_folder_exit,void,BrowserWorker* +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* +Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" +Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" +Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" +Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" +Function,+,file_browser_worker_set_long_load_callback,void,"BrowserWorker*, BrowserWorkerLongLoadCallback" +Function,+,file_stream_alloc,Stream*,Storage* +Function,+,file_stream_close,_Bool,Stream* +Function,+,file_stream_get_error,FS_Error,Stream* +Function,+,file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,-,fileno,int,FILE* +Function,-,fileno_unlocked,int,FILE* +Function,+,filesystem_api_error_get_desc,const char*,FS_Error +Function,-,fiprintf,int,"FILE*, const char*, ..." +Function,-,fiscanf,int,"FILE*, const char*, ..." +Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_free,void,FlipperApplication* +Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus +Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" +Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* +Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_buffered_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_delete_key,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_alloc,FlipperFormat*,Storage* +Function,+,flipper_format_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_file_open_always,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_append,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_new,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_free,void,FlipperFormat* +Function,+,flipper_format_get_raw_stream,Stream*,FlipperFormat* +Function,+,flipper_format_get_value_count,_Bool,"FlipperFormat*, const char*, uint32_t*" +Function,+,flipper_format_insert_or_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_insert_or_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_insert_or_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_insert_or_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_insert_or_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_key_exist,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_read_bool,_Bool,"FlipperFormat*, const char*, _Bool*, const uint16_t" +Function,+,flipper_format_read_float,_Bool,"FlipperFormat*, const char*, float*, const uint16_t" +Function,+,flipper_format_read_header,_Bool,"FlipperFormat*, FuriString*, uint32_t*" +Function,+,flipper_format_read_hex,_Bool,"FlipperFormat*, const char*, uint8_t*, const uint16_t" +Function,+,flipper_format_read_hex_uint64,_Bool,"FlipperFormat*, const char*, uint64_t*, const uint16_t" +Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t*, const uint16_t" +Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" +Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* +Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" +Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" +Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" +Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" +Function,+,flipper_format_write_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_write_hex_uint64,_Bool,"FlipperFormat*, const char*, const uint64_t*, const uint16_t" +Function,+,flipper_format_write_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_write_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,float_is_equal,_Bool,"float, float" +Function,-,flockfile,void,FILE* +Function,-,fls,int,int +Function,-,flsl,int,long +Function,-,flsll,int,long long +Function,-,fmemopen,FILE*,"void*, size_t, const char*" +Function,-,fopen,FILE*,"const char*, const char*" +Function,-,fopencookie,FILE*,"void*, const char*, cookie_io_functions_t" +Function,-,fprintf,int,"FILE*, const char*, ..." +Function,-,fpurge,int,FILE* +Function,-,fputc,int,"int, FILE*" +Function,-,fputc_unlocked,int,"int, FILE*" +Function,-,fputs,int,"const char*, FILE*" +Function,-,fputs_unlocked,int,"const char*, FILE*" +Function,-,fread,size_t,"void*, size_t, size_t, FILE*" +Function,-,fread_unlocked,size_t,"void*, size_t, size_t, FILE*" +Function,+,free,void,void* +Function,-,freopen,FILE*,"const char*, const char*, FILE*" +Function,-,fscanf,int,"FILE*, const char*, ..." +Function,-,fseek,int,"FILE*, long, int" +Function,-,fseeko,int,"FILE*, off_t, int" +Function,-,fsetpos,int,"FILE*, const fpos_t*" +Function,-,ftell,long,FILE* +Function,-,ftello,off_t,FILE* +Function,-,ftrylockfile,int,FILE* +Function,-,funlockfile,void,FILE* +Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,+,furi_delay_ms,void,uint32_t +Function,+,furi_delay_tick,void,uint32_t +Function,+,furi_delay_until_tick,FuriStatus,uint32_t +Function,+,furi_delay_us,void,uint32_t +Function,+,furi_event_flag_alloc,FuriEventFlag*, +Function,+,furi_event_flag_clear,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_free,void,FuriEventFlag* +Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* +Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,+,furi_get_tick,uint32_t, +Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_clear_white_list,_Bool, +Function,+,furi_hal_bt_dump_state,void,FuriString* +Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, +Function,+,furi_hal_bt_get_rssi,float, +Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, +Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_bt_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_bt_hid_start,void, +Function,+,furi_hal_bt_hid_stop,void, +Function,-,furi_hal_bt_init,void, +Function,+,furi_hal_bt_is_active,_Bool, +Function,+,furi_hal_bt_is_alive,_Bool, +Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_testing_supported,_Bool, +Function,+,furi_hal_bt_lock_core2,void, +Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, +Function,+,furi_hal_bt_nvm_sram_sem_release,void, +Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, +Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus +Function,+,furi_hal_bt_serial_start,void, +Function,+,furi_hal_bt_serial_stop,void, +Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" +Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,furi_hal_bt_start_advertising,void, +Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_start_packet_tx,void,"uint8_t, uint8_t, uint8_t" +Function,+,furi_hal_bt_start_radio_stack,_Bool, +Function,+,furi_hal_bt_start_rx,void,uint8_t +Function,+,furi_hal_bt_start_tone_tx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_stop_advertising,void, +Function,+,furi_hal_bt_stop_packet_test,uint16_t, +Function,+,furi_hal_bt_stop_rx,void, +Function,+,furi_hal_bt_stop_tone_tx,void, +Function,+,furi_hal_bt_unlock_core2,void, +Function,+,furi_hal_bt_update_battery_level,void,uint8_t +Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t +Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t +Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" +Function,-,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_init,void, +Function,-,furi_hal_clock_init_early,void, +Function,+,furi_hal_clock_mco_disable,void, +Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" +Function,-,furi_hal_clock_resume_tick,void, +Function,-,furi_hal_clock_suspend_tick,void, +Function,-,furi_hal_clock_switch_to_hsi,void, +Function,-,furi_hal_clock_switch_to_pll,void, +Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t +Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,furi_hal_compress_free,void,FuriHalCompress* +Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" +Function,-,furi_hal_compress_icon_init,void, +Function,+,furi_hal_console_disable,void, +Function,+,furi_hal_console_enable,void, +Function,+,furi_hal_console_init,void, +Function,+,furi_hal_console_printf,void,"const char[], ..." +Function,+,furi_hal_console_puts,void,const char* +Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" +Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" +Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" +Function,+,furi_hal_cortex_delay_us,void,uint32_t +Function,-,furi_hal_cortex_init_early,void, +Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,-,furi_hal_crypto_init,void, +Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_debug_disable,void, +Function,+,furi_hal_debug_enable,void, +Function,-,furi_hal_deinit_early,void, +Function,-,furi_hal_flash_erase,void,uint8_t +Function,-,furi_hal_flash_get_base,size_t, +Function,-,furi_hal_flash_get_cycles_count,size_t, +Function,-,furi_hal_flash_get_free_end_address,const void*, +Function,-,furi_hal_flash_get_free_page_count,size_t, +Function,-,furi_hal_flash_get_free_page_start_address,size_t, +Function,-,furi_hal_flash_get_free_start_address,const void*, +Function,-,furi_hal_flash_get_page_number,int16_t,size_t +Function,-,furi_hal_flash_get_page_size,size_t, +Function,-,furi_hal_flash_get_read_block_size,size_t, +Function,-,furi_hal_flash_get_write_block_size,size_t, +Function,-,furi_hal_flash_init,void, +Function,-,furi_hal_flash_ob_apply,void, +Function,-,furi_hal_flash_ob_get_raw_ptr,const FuriHalFlashRawOptionByteData*, +Function,-,furi_hal_flash_ob_set_word,_Bool,"size_t, const uint32_t" +Function,-,furi_hal_flash_program_page,void,"const uint8_t, const uint8_t*, uint16_t" +Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" +Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" +Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" +Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" +Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" +Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin* +Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_hid_get_led_state,uint8_t, +Function,+,furi_hal_hid_is_connected,_Bool, +Function,+,furi_hal_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release_all,_Bool, +Function,+,furi_hal_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_hid_set_state_callback,void,"HidStateCallback, void*" +Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* +Function,+,furi_hal_hid_u2f_is_connected,_Bool, +Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" +Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" +Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,-,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_init,void, +Function,-,furi_hal_i2c_init_early,void, +Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" +Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_init,void, +Function,-,furi_hal_init_early,void, +Function,-,furi_hal_interrupt_init,void, +Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_light_blink_set_color,void,Light +Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" +Function,+,furi_hal_light_blink_stop,void, +Function,-,furi_hal_light_init,void, +Function,+,furi_hal_light_sequence,void,const char* +Function,+,furi_hal_light_set,void,"Light, uint8_t" +Function,+,furi_hal_memory_alloc,void*,size_t +Function,+,furi_hal_memory_get_free,size_t, +Function,+,furi_hal_memory_init,void, +Function,+,furi_hal_memory_max_pool_block,size_t, +Function,+,furi_hal_mpu_disable,void, +Function,+,furi_hal_mpu_enable,void, +Function,-,furi_hal_mpu_init,void, +Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion +Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" +Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" +Function,-,furi_hal_os_init,void, +Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_status,void, +Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_deep_sleep_available,_Bool, +Function,+,furi_hal_power_disable_external_3_3v,void, +Function,+,furi_hal_power_disable_otg,void, +Function,+,furi_hal_power_enable_external_3_3v,void, +Function,+,furi_hal_power_enable_otg,void, +Function,+,furi_hal_power_gauge_is_ok,_Bool, +Function,+,furi_hal_power_get_bat_health_pct,uint8_t, +Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_remaining_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC +Function,+,furi_hal_power_get_pct,uint8_t, +Function,+,furi_hal_power_get_usb_voltage,float, +Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_power_init,void, +Function,+,furi_hal_power_insomnia_enter,void, +Function,+,furi_hal_power_insomnia_exit,void, +Function,-,furi_hal_power_insomnia_level,uint16_t, +Function,+,furi_hal_power_is_charging,_Bool, +Function,+,furi_hal_power_is_charging_done,_Bool, +Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_off,void, +Function,+,furi_hal_power_reset,void, +Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_shutdown,void, +Function,+,furi_hal_power_sleep,void, +Function,+,furi_hal_power_sleep_available,_Bool, +Function,+,furi_hal_power_suppress_charge_enter,void, +Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId +Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" +Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_region_get,const FuriHalRegion*, +Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t +Function,+,furi_hal_region_get_name,const char*, +Function,-,furi_hal_region_init,void, +Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t +Function,+,furi_hal_region_is_provisioned,_Bool, +Function,+,furi_hal_region_set,void,FuriHalRegion* +Function,-,furi_hal_resources_deinit_early,void, +Function,-,furi_hal_resources_init,void, +Function,-,furi_hal_resources_init_early,void, +Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* +Function,-,furi_hal_rtc_deinit_early,void, +Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, +Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, +Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, +Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, +Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_level,uint8_t, +Function,+,furi_hal_rtc_get_pin_fails,uint32_t, +Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister +Function,+,furi_hal_rtc_get_timestamp,uint32_t, +Function,-,furi_hal_rtc_init,void, +Function,-,furi_hal_rtc_init_early,void, +Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode +Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_set_fault_data,void,uint32_t +Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode +Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat +Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat +Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_level,void,uint8_t +Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t +Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, +Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, +Function,+,furi_hal_speaker_set_volume,void,float +Function,+,furi_hal_speaker_start,void,"float, float" +Function,+,furi_hal_speaker_stop,void, +Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* +Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* +Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,-,furi_hal_spi_config_deinit_early,void, +Function,-,furi_hal_spi_config_init,void, +Function,-,furi_hal_spi_config_init_early,void, +Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_switch,void,void* +Function,+,furi_hal_uart_deinit,void,FuriHalUartId +Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_resume,void,FuriHalUartId +Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" +Function,+,furi_hal_uart_suspend,void,FuriHalUartId +Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" +Function,+,furi_hal_usb_disable,void, +Function,+,furi_hal_usb_enable,void, +Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,-,furi_hal_usb_init,void, +Function,+,furi_hal_usb_is_locked,_Bool, +Function,+,furi_hal_usb_lock,void, +Function,+,furi_hal_usb_reinit,void, +Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" +Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_unlock,void, +Function,+,furi_hal_version_do_i_belong_here,_Bool, +Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, +Function,+,furi_hal_version_get_ble_mac,const uint8_t*, +Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, +Function,+,furi_hal_version_get_firmware_version,const Version*, +Function,+,furi_hal_version_get_hw_body,uint8_t, +Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, +Function,+,furi_hal_version_get_hw_connect,uint8_t, +Function,+,furi_hal_version_get_hw_display,FuriHalVersionDisplay, +Function,+,furi_hal_version_get_hw_region,FuriHalVersionRegion, +Function,+,furi_hal_version_get_hw_region_name,const char*, +Function,+,furi_hal_version_get_hw_target,uint8_t, +Function,+,furi_hal_version_get_hw_timestamp,uint32_t, +Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, +Function,+,furi_hal_version_get_model_name,const char*, +Function,+,furi_hal_version_get_name_ptr,const char*, +Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, +Function,-,furi_hal_version_init,void, +Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_size,size_t, +Function,-,furi_hal_vibro_init,void, +Function,+,furi_hal_vibro_on,void,_Bool +Function,-,furi_init,void, +Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, +Function,+,furi_kernel_lock,int32_t, +Function,+,furi_kernel_restore_lock,int32_t,int32_t +Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_get_level,FuriLogLevel, +Function,-,furi_log_init,void, +Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_set_level,void,FuriLogLevel +Function,-,furi_log_set_puts,void,FuriLogPuts +Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" +Function,+,furi_message_queue_free,void,FuriMessageQueue* +Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" +Function,+,furi_message_queue_get_capacity,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_count,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_message_size,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_space,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_put,FuriStatus,"FuriMessageQueue*, const void*, uint32_t" +Function,+,furi_message_queue_reset,FuriStatus,FuriMessageQueue* +Function,+,furi_ms_to_ticks,uint32_t,uint32_t +Function,+,furi_mutex_acquire,FuriStatus,"FuriMutex*, uint32_t" +Function,+,furi_mutex_alloc,FuriMutex*,FuriMutexType +Function,+,furi_mutex_free,void,FuriMutex* +Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* +Function,+,furi_mutex_release,FuriStatus,FuriMutex* +Function,+,furi_pubsub_alloc,FuriPubSub*, +Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" +Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" +Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" +Function,+,furi_record_close,void,const char* +Function,+,furi_record_create,void,"const char*, void*" +Function,-,furi_record_destroy,_Bool,const char* +Function,+,furi_record_exists,_Bool,const char* +Function,-,furi_record_init,void, +Function,+,furi_record_open,void*,const char* +Function,+,furi_run,void, +Function,+,furi_semaphore_acquire,FuriStatus,"FuriSemaphore*, uint32_t" +Function,+,furi_semaphore_alloc,FuriSemaphore*,"uint32_t, uint32_t" +Function,+,furi_semaphore_free,void,FuriSemaphore* +Function,+,furi_semaphore_get_count,uint32_t,FuriSemaphore* +Function,+,furi_semaphore_release,FuriStatus,FuriSemaphore* +Function,+,furi_stream_buffer_alloc,FuriStreamBuffer*,"size_t, size_t" +Function,+,furi_stream_buffer_bytes_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_buffer_free,void,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_empty,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_full,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, uint32_t" +Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* +Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" +Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" +Function,+,furi_string_alloc,FuriString*, +Function,+,furi_string_alloc_move,FuriString*,FuriString* +Function,+,furi_string_alloc_printf,FuriString*,"const char[], ..." +Function,+,furi_string_alloc_set,FuriString*,const FuriString* +Function,+,furi_string_alloc_set_str,FuriString*,const char[] +Function,+,furi_string_alloc_vprintf,FuriString*,"const char[], va_list" +Function,+,furi_string_cat,void,"FuriString*, const FuriString*" +Function,+,furi_string_cat_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_cat_str,void,"FuriString*, const char[]" +Function,+,furi_string_cat_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_string_cmp,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmp_str,int,"const FuriString*, const char[]" +Function,+,furi_string_cmpi,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmpi_str,int,"const FuriString*, const char[]" +Function,+,furi_string_empty,_Bool,const FuriString* +Function,+,furi_string_end_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_end_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_equal,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_equal_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_free,void,FuriString* +Function,+,furi_string_get_char,char,"const FuriString*, size_t" +Function,+,furi_string_get_cstr,const char*,const FuriString* +Function,+,furi_string_hash,size_t,const FuriString* +Function,+,furi_string_left,void,"FuriString*, size_t" +Function,+,furi_string_mid,void,"FuriString*, size_t, size_t" +Function,+,furi_string_move,void,"FuriString*, FuriString*" +Function,+,furi_string_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_push_back,void,"FuriString*, char" +Function,+,furi_string_replace,size_t,"FuriString*, FuriString*, FuriString*, size_t" +Function,+,furi_string_replace_all,void,"FuriString*, const FuriString*, const FuriString*" +Function,+,furi_string_replace_all_str,void,"FuriString*, const char[], const char[]" +Function,+,furi_string_replace_at,void,"FuriString*, size_t, size_t, const char[]" +Function,+,furi_string_replace_str,size_t,"FuriString*, const char[], const char[], size_t" +Function,+,furi_string_reserve,void,"FuriString*, size_t" +Function,+,furi_string_reset,void,FuriString* +Function,+,furi_string_right,void,"FuriString*, size_t" +Function,+,furi_string_search,size_t,"const FuriString*, const FuriString*, size_t" +Function,+,furi_string_search_char,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_rchar,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_str,size_t,"const FuriString*, const char[], size_t" +Function,+,furi_string_set,void,"FuriString*, FuriString*" +Function,+,furi_string_set_char,void,"FuriString*, size_t, const char" +Function,+,furi_string_set_n,void,"FuriString*, const FuriString*, size_t, size_t" +Function,+,furi_string_set_str,void,"FuriString*, const char[]" +Function,+,furi_string_set_strn,void,"FuriString*, const char[], size_t" +Function,+,furi_string_size,size_t,const FuriString* +Function,+,furi_string_start_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_start_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_swap,void,"FuriString*, FuriString*" +Function,+,furi_string_trim,void,"FuriString*, const char[]" +Function,+,furi_string_utf8_decode,void,"char, FuriStringUTF8State*, FuriStringUnicodeValue*" +Function,+,furi_string_utf8_length,size_t,FuriString* +Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" +Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,+,furi_thread_catch,void, +Function,-,furi_thread_disable_heap_trace,void,FuriThread* +Function,+,furi_thread_enable_heap_trace,void,FuriThread* +Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" +Function,+,furi_thread_flags_clear,uint32_t,uint32_t +Function,+,furi_thread_flags_get,uint32_t, +Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" +Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" +Function,+,furi_thread_free,void,FuriThread* +Function,+,furi_thread_get_current,FuriThread*, +Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_heap_size,size_t,FuriThread* +Function,+,furi_thread_get_id,FuriThreadId,FuriThread* +Function,+,furi_thread_get_name,const char*,FuriThreadId +Function,+,furi_thread_get_return_code,int32_t,FuriThread* +Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId +Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +Function,+,furi_thread_is_suspended,_Bool,FuriThreadId +Function,+,furi_thread_join,_Bool,FuriThread* +Function,+,furi_thread_mark_as_service,void,FuriThread* +Function,+,furi_thread_resume,void,FuriThreadId +Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" +Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_name,void,"FuriThread*, const char*" +Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" +Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" +Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" +Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_stdout_callback,_Bool,FuriThreadStdoutWriteCallback +Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdout_flush,int32_t, +Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" +Function,+,furi_thread_suspend,void,FuriThreadId +Function,+,furi_thread_yield,void, +Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" +Function,+,furi_timer_free,void,FuriTimer* +Function,+,furi_timer_is_running,uint32_t,FuriTimer* +Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_stop,FuriStatus,FuriTimer* +Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" +Function,-,fwrite_unlocked,size_t,"const void*, size_t, size_t, FILE*" +Function,-,gap_get_state,GapState, +Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" +Function,-,gap_start_advertising,void, +Function,-,gap_stop_advertising,void, +Function,-,gap_thread_stop,void, +Function,-,getc,int,FILE* +Function,-,getc_unlocked,int,FILE* +Function,-,getchar,int, +Function,-,getchar_unlocked,int, +Function,-,getenv,char*,const char* +Function,-,gets,char*,char* +Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getw,int,FILE* +Function,-,gmtime,tm*,const time_t* +Function,-,gmtime_r,tm*,"const time_t*, tm*" +Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" +Function,+,gui_direct_draw_acquire,Canvas*,Gui* +Function,+,gui_direct_draw_release,void,Gui* +Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" +Function,+,gui_set_lockdown,void,"Gui*, _Bool" +Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*" +Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" +Function,+,hal_sd_detect,_Bool, +Function,+,hal_sd_detect_init,void, +Function,+,hal_sd_detect_set_low,void, +Function,+,hmac_sha256_finish,void,"const hmac_sha256_context*, const uint8_t*, uint8_t*" +Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" +Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" +Function,+,icon_animation_alloc,IconAnimation*,const Icon* +Function,+,icon_animation_free,void,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,IconAnimation* +Function,+,icon_animation_get_width,uint8_t,IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" +Function,+,icon_animation_start,void,IconAnimation* +Function,+,icon_animation_stop,void,IconAnimation* +Function,+,icon_get_data,const uint8_t*,const Icon* +Function,+,icon_get_height,uint8_t,const Icon* +Function,+,icon_get_width,uint8_t,const Icon* +Function,-,index,char*,"const char*, int" +Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t" +Function,-,initstate,char*,"unsigned, char*, size_t" +Function,+,input_get_key_name,const char*,InputKey +Function,+,input_get_type_name,const char*,InputType +Function,-,iprintf,int,"const char*, ..." +Function,-,isalnum,int,int +Function,-,isalnum_l,int,"int, locale_t" +Function,-,isalpha,int,int +Function,-,isalpha_l,int,"int, locale_t" +Function,-,isascii,int,int +Function,-,isascii_l,int,"int, locale_t" +Function,-,isblank,int,int +Function,-,isblank_l,int,"int, locale_t" +Function,-,iscanf,int,"const char*, ..." +Function,-,iscntrl,int,int +Function,-,iscntrl_l,int,"int, locale_t" +Function,-,isdigit,int,int +Function,-,isdigit_l,int,"int, locale_t" +Function,-,isgraph,int,int +Function,-,isgraph_l,int,"int, locale_t" +Function,-,islower,int,int +Function,-,islower_l,int,"int, locale_t" +Function,-,isprint,int,int +Function,-,isprint_l,int,"int, locale_t" +Function,-,ispunct,int,int +Function,-,ispunct_l,int,"int, locale_t" +Function,-,isspace,int,int +Function,-,isspace_l,int,"int, locale_t" +Function,-,isupper,int,int +Function,-,isupper_l,int,"int, locale_t" +Function,-,isxdigit,int,int +Function,-,isxdigit_l,int,"int, locale_t" +Function,-,itoa,char*,"int, char*, int" +Function,-,jrand48,long,unsigned short[3] +Function,-,l64a,char*,long +Function,-,labs,long,long +Function,-,lcong48,void,unsigned short[7] +Function,-,ldiv,ldiv_t,"long, long" +Function,-,llabs,long long,long long +Function,-,lldiv,lldiv_t,"long long, long long" +Function,+,loader_get_pubsub,FuriPubSub*,Loader* +Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_lock,_Bool,Loader* +Function,+,loader_show_menu,void, +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_unlock,void,Loader* +Function,+,loader_update_menu,void, +Function,+,loading_alloc,Loading*, +Function,+,loading_free,void,Loading* +Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleDateFormat, const char*" +Function,+,locale_format_time,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleTimeFormat, const _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnits, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits +Function,+,locale_set_time_format,void,LocaleTimeFormat +Function,-,localtime,tm*,const time_t* +Function,-,localtime_r,tm*,"const time_t*, tm*" +Function,-,lrand48,long, +Function,+,malloc,void*,size_t +Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, ManchesterState*, _Bool*" +Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*" +Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState* +Function,+,manchester_encoder_reset,void,ManchesterEncoderState* +Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des3_free,void,mbedtls_des3_context* +Function,-,mbedtls_des3_init,void,mbedtls_des3_context* +Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des_free,void,mbedtls_des_context* +Function,-,mbedtls_des_init,void,mbedtls_des_context* +Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8] +Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8] +Function,-,mbedtls_des_key_set_parity,void,unsigned char[8] +Function,-,mbedtls_des_self_test,int,int +Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]" +Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]" +Function,-,mbedtls_platform_gmtime_r,tm*,"const mbedtls_time_t*, tm*" +Function,-,mbedtls_platform_zeroize,void,"void*, size_t" +Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]" +Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*" +Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]" +Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_self_test,int,int +Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context* +Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t" +Function,-,mblen,int,"const char*, size_t" +Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" +Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,+,md5,void,"const unsigned char*, size_t, unsigned char[16]" +Function,+,md5_finish,void,"md5_context*, unsigned char[16]" +Function,+,md5_process,void,"md5_context*, const unsigned char[64]" +Function,+,md5_starts,void,md5_context* +Function,+,md5_update,void,"md5_context*, const unsigned char*, size_t" +Function,-,memccpy,void*,"void*, const void*, int, size_t" +Function,+,memchr,void*,"const void*, int, size_t" +Function,+,memcmp,int,"const void*, const void*, size_t" +Function,+,memcpy,void*,"void*, const void*, size_t" +Function,-,memmem,void*,"const void*, size_t, const void*, size_t" +Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_get_free_heap,size_t, +Function,+,memmgr_get_minimum_free_heap,size_t, +Function,+,memmgr_get_total_heap,size_t, +Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_get_max_free_block,size_t, +Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, +Function,-,memmgr_pool_get_max_block,size_t, +Function,+,memmove,void*,"void*, const void*, size_t" +Function,-,mempcpy,void*,"void*, const void*, size_t" +Function,-,memrchr,void*,"const void*, int, size_t" +Function,+,memset,void*,"void*, int, size_t" +Function,+,menu_add_item,void,"Menu*, const char*, const Icon*, uint32_t, MenuItemCallback, void*" +Function,+,menu_alloc,Menu*, +Function,+,menu_free,void,Menu* +Function,+,menu_get_view,View*,Menu* +Function,+,menu_reset,void,Menu* +Function,+,menu_set_selected_item,void,"Menu*, uint32_t" +Function,-,mkdtemp,char*,char* +Function,-,mkostemp,int,"char*, int" +Function,-,mkostemps,int,"char*, int, int" +Function,-,mkstemp,int,char* +Function,-,mkstemps,int,"char*, int" +Function,-,mktemp,char*,char* +Function,-,mktime,time_t,tm* +Function,-,mrand48,long, +Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" +Function,-,nrand48,long,unsigned short[3] +Function,-,on_exit,int,"void (*)(int, void*), void*" +Function,-,open_memstream,FILE*,"char**, size_t*" +Function,+,path_append,void,"FuriString*, const char*" +Function,+,path_concat,void,"const char*, const char*, FuriString*" +Function,+,path_contains_only_ascii,_Bool,const char* +Function,+,path_extract_basename,void,"const char*, FuriString*" +Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_extension,void,"FuriString*, char*, size_t" +Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" +Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,-,pcTaskGetName,char*,TaskHandle_t +Function,-,pcTimerGetName,const char*,TimerHandle_t +Function,-,pclose,int,FILE* +Function,-,perror,void,const char* +Function,-,popen,FILE*,"const char*, const char*" +Function,+,popup_alloc,Popup*, +Function,+,popup_disable_timeout,void,Popup* +Function,+,popup_enable_timeout,void,Popup* +Function,+,popup_free,void,Popup* +Function,+,popup_get_view,View*,Popup* +Function,+,popup_reset,void,Popup* +Function,+,popup_set_callback,void,"Popup*, PopupCallback" +Function,+,popup_set_context,void,"Popup*, void*" +Function,+,popup_set_header,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_icon,void,"Popup*, uint8_t, uint8_t, const Icon*" +Function,+,popup_set_text,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_timeout,void,"Popup*, uint32_t" +Function,-,posix_memalign,int,"void**, size_t, size_t" +Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_get_info,void,"Power*, PowerInfo*" +Function,+,power_get_pubsub,FuriPubSub*,Power* +Function,+,power_is_battery_healthy,_Bool,Power* +Function,+,power_off,void,Power* +Function,+,power_reboot,void,PowerBootMode +Function,-,printf,int,"const char*, ..." +Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_start,void,ProtocolDict* +Function,+,protocol_dict_encoder_start,_Bool,"ProtocolDict*, size_t" +Function,+,protocol_dict_encoder_yield,LevelDuration,"ProtocolDict*, size_t" +Function,+,protocol_dict_free,void,ProtocolDict* +Function,+,protocol_dict_get_data,void,"ProtocolDict*, size_t, uint8_t*, size_t" +Function,+,protocol_dict_get_data_size,size_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_features,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_manufacturer,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_max_data_size,size_t,ProtocolDict* +Function,+,protocol_dict_get_name,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_protocol_by_name,ProtocolId,"ProtocolDict*, const char*" +Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" +Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,-,putc,int,"int, FILE*" +Function,-,putc_unlocked,int,"int, FILE*" +Function,-,putchar,int,int +Function,-,putchar_unlocked,int,int +Function,-,putenv,int,char* +Function,-,puts,int,const char* +Function,-,putw,int,"int, FILE*" +Function,-,pvPortCalloc,void*,"size_t, size_t" +Function,-,pvPortMalloc,void*,size_t +Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" +Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, +Function,-,pvTimerGetTimerID,void*,const TimerHandle_t +Function,-,pxPortInitialiseStack,StackType_t*,"StackType_t*, TaskFunction_t, void*" +Function,-,qsort,void,"void*, size_t, size_t, __compar_fn_t" +Function,-,qsort_r,void,"void*, size_t, size_t, int (*)(const void*, const void*, void*), void*" +Function,-,quick_exit,void,int +Function,+,rand,int, +Function,-,rand_r,int,unsigned* +Function,+,random,long, +Function,-,rawmemchr,void*,"const void*, int" +Function,-,read_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" +Function,+,realloc,void*,"void*, size_t" +Function,-,reallocarray,void*,"void*, size_t, size_t" +Function,-,reallocf,void*,"void*, size_t" +Function,-,realpath,char*,"const char*, char*" +Function,+,release_mutex,_Bool,"ValueMutex*, const void*" +Function,-,remove,int,const char* +Function,-,rename,int,"const char*, const char*" +Function,-,renameat,int,"int, const char*, int, const char*" +Function,-,rewind,void,FILE* +Function,-,rindex,char*,"const char*, int" +Function,+,rpc_session_close,void,RpcSession* +Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" +Function,+,rpc_session_get_available_size,size_t,RpcSession* +Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" +Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" +Function,+,rpc_session_set_context,void,"RpcSession*, void*" +Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" +Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" +Function,+,rpc_system_app_error_reset,void,RpcAppSystem* +Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" +Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* +Function,+,rpc_system_app_send_exited,void,RpcAppSystem* +Function,+,rpc_system_app_send_started,void,RpcAppSystem* +Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" +Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*" +Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" +Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" +Function,-,rpmatch,int,const char* +Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,-,scanf,int,"const char*, ..." +Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" +Function,+,scene_manager_free,void,SceneManager* +Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_handle_back_event,_Bool,SceneManager* +Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_handle_tick_event,void,SceneManager* +Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" +Function,+,scene_manager_previous_scene,_Bool,SceneManager* +Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene_one_of,_Bool,"SceneManager*, const uint32_t*, size_t" +Function,+,scene_manager_set_scene_state,void,"SceneManager*, uint32_t, uint32_t" +Function,+,scene_manager_stop,void,SceneManager* +Function,+,sd_api_get_fs_type_text,const char*,SDFsType +Function,-,secure_getenv,char*,const char* +Function,-,seed48,unsigned short*,unsigned short[3] +Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" +Function,-,serial_svc_is_started,_Bool, +Function,-,serial_svc_notify_buffer_is_empty,void, +Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,-,serial_svc_set_rpc_status,void,SerialServiceRpcStatus +Function,-,serial_svc_start,void, +Function,-,serial_svc_stop,void, +Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" +Function,+,set_random_name,void,"char*, uint8_t" +Function,-,setbuf,void,"FILE*, char*" +Function,-,setbuffer,void,"FILE*, char*, int" +Function,-,setenv,int,"const char*, const char*, int" +Function,-,setkey,void,const char* +Function,-,setlinebuf,int,FILE* +Function,-,setstate,char*,char* +Function,-,setvbuf,int,"FILE*, char*, int, size_t" +Function,+,sha256,void,"const unsigned char*, unsigned int, unsigned char[32]" +Function,+,sha256_finish,void,"sha256_context*, unsigned char[32]" +Function,+,sha256_process,void,sha256_context* +Function,+,sha256_start,void,sha256_context* +Function,+,sha256_update,void,"sha256_context*, const unsigned char*, unsigned int" +Function,-,siprintf,int,"char*, const char*, ..." +Function,-,siscanf,int,"const char*, const char*, ..." +Function,-,sniprintf,int,"char*, size_t, const char*, ..." +Function,+,snprintf,int,"char*, size_t, const char*, ..." +Function,-,sprintf,int,"char*, const char*, ..." +Function,+,srand,void,unsigned +Function,-,srand48,void,long +Function,-,srandom,void,unsigned +Function,+,sscanf,int,"const char*, const char*, ..." +Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" +Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" +Function,+,storage_common_remove,FS_Error,"Storage*, const char*" +Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" +Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" +Function,+,storage_dir_close,_Bool,File* +Function,+,storage_dir_open,_Bool,"File*, const char*" +Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" +Function,-,storage_dir_rewind,_Bool,File* +Function,+,storage_error_get_desc,const char*,FS_Error +Function,+,storage_file_alloc,File*,Storage* +Function,+,storage_file_close,_Bool,File* +Function,+,storage_file_eof,_Bool,File* +Function,+,storage_file_exists,_Bool,"Storage*, const char*" +Function,+,storage_file_free,void,File* +Function,+,storage_file_get_error,FS_Error,File* +Function,+,storage_file_get_error_desc,const char*,File* +Function,-,storage_file_get_internal_error,int32_t,File* +Function,+,storage_file_is_dir,_Bool,File* +Function,+,storage_file_is_open,_Bool,File* +Function,+,storage_file_open,_Bool,"File*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,storage_file_read,uint16_t,"File*, void*, uint16_t" +Function,+,storage_file_seek,_Bool,"File*, uint32_t, _Bool" +Function,+,storage_file_size,uint64_t,File* +Function,-,storage_file_sync,_Bool,File* +Function,+,storage_file_tell,uint64_t,File* +Function,+,storage_file_truncate,_Bool,File* +Function,+,storage_file_write,uint16_t,"File*, const void*, uint16_t" +Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t" +Function,+,storage_get_pubsub,FuriPubSub*,Storage* +Function,+,storage_int_backup,FS_Error,"Storage*, const char*" +Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter" +Function,+,storage_sd_format,FS_Error,Storage* +Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*" +Function,+,storage_sd_status,FS_Error,Storage* +Function,+,storage_sd_unmount,FS_Error,Storage* +Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*" +Function,+,storage_simply_remove,_Bool,"Storage*, const char*" +Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" +Function,-,stpcpy,char*,"char*, const char*" +Function,-,stpncpy,char*,"char*, const char*, size_t" +Function,-,strcasecmp,int,"const char*, const char*" +Function,-,strcasecmp_l,int,"const char*, const char*, locale_t" +Function,+,strcasestr,char*,"const char*, const char*" +Function,-,strcat,char*,"char*, const char*" +Function,+,strchr,char*,"const char*, int" +Function,-,strchrnul,char*,"const char*, int" +Function,+,strcmp,int,"const char*, const char*" +Function,-,strcoll,int,"const char*, const char*" +Function,-,strcoll_l,int,"const char*, const char*, locale_t" +Function,+,strcpy,char*,"char*, const char*" +Function,+,strcspn,size_t,"const char*, const char*" +Function,+,strdup,char*,const char* +Function,+,stream_clean,void,Stream* +Function,+,stream_copy,size_t,"Stream*, Stream*, size_t" +Function,+,stream_copy_full,size_t,"Stream*, Stream*" +Function,+,stream_delete,_Bool,"Stream*, size_t" +Function,+,stream_delete_and_insert,_Bool,"Stream*, size_t, StreamWriteCB, const void*" +Function,+,stream_delete_and_insert_char,_Bool,"Stream*, size_t, char" +Function,+,stream_delete_and_insert_cstring,_Bool,"Stream*, size_t, const char*" +Function,+,stream_delete_and_insert_format,_Bool,"Stream*, size_t, const char*, ..." +Function,+,stream_delete_and_insert_string,_Bool,"Stream*, size_t, FuriString*" +Function,+,stream_delete_and_insert_vaformat,_Bool,"Stream*, size_t, const char*, va_list" +Function,+,stream_dump_data,void,Stream* +Function,+,stream_eof,_Bool,Stream* +Function,+,stream_free,void,Stream* +Function,+,stream_insert,_Bool,"Stream*, const uint8_t*, size_t" +Function,+,stream_insert_char,_Bool,"Stream*, char" +Function,+,stream_insert_cstring,_Bool,"Stream*, const char*" +Function,+,stream_insert_format,_Bool,"Stream*, const char*, ..." +Function,+,stream_insert_string,_Bool,"Stream*, FuriString*" +Function,+,stream_insert_vaformat,_Bool,"Stream*, const char*, va_list" +Function,+,stream_load_from_file,size_t,"Stream*, Storage*, const char*" +Function,+,stream_read,size_t,"Stream*, uint8_t*, size_t" +Function,+,stream_read_line,_Bool,"Stream*, FuriString*" +Function,+,stream_rewind,_Bool,Stream* +Function,+,stream_save_to_file,size_t,"Stream*, Storage*, const char*, FS_OpenMode" +Function,+,stream_seek,_Bool,"Stream*, int32_t, StreamOffset" +Function,+,stream_seek_to_char,_Bool,"Stream*, char, StreamDirection" +Function,+,stream_size,size_t,Stream* +Function,+,stream_split,_Bool,"Stream*, Stream*, Stream*" +Function,+,stream_tell,size_t,Stream* +Function,+,stream_write,size_t,"Stream*, const uint8_t*, size_t" +Function,+,stream_write_char,size_t,"Stream*, char" +Function,+,stream_write_cstring,size_t,"Stream*, const char*" +Function,+,stream_write_format,size_t,"Stream*, const char*, ..." +Function,+,stream_write_string,size_t,"Stream*, FuriString*" +Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list" +Function,-,strerror,char*,int +Function,-,strerror_l,char*,"int, locale_t" +Function,-,strerror_r,char*,"int, char*, size_t" +Function,-,strftime,size_t,"char*, size_t, const char*, const tm*" +Function,-,strftime_l,size_t,"char*, size_t, const char*, const tm*, locale_t" +Function,+,string_stream_alloc,Stream*, +Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strlcpy,size_t,"char*, const char*, size_t" +Function,+,strlen,size_t,const char* +Function,-,strlwr,char*,char* +Function,+,strncasecmp,int,"const char*, const char*, size_t" +Function,-,strncasecmp_l,int,"const char*, const char*, size_t, locale_t" +Function,-,strncat,char*,"char*, const char*, size_t" +Function,+,strncmp,int,"const char*, const char*, size_t" +Function,+,strncpy,char*,"char*, const char*, size_t" +Function,-,strndup,char*,"const char*, size_t" +Function,-,strnlen,size_t,"const char*, size_t" +Function,-,strnstr,char*,"const char*, const char*, size_t" +Function,-,strpbrk,char*,"const char*, const char*" +Function,-,strptime,char*,"const char*, const char*, tm*" +Function,-,strptime_l,char*,"const char*, const char*, tm*, locale_t" +Function,+,strrchr,char*,"const char*, int" +Function,-,strsep,char*,"char**, const char*" +Function,-,strsignal,char*,int +Function,+,strspn,size_t,"const char*, const char*" +Function,+,strstr,char*,"const char*, const char*" +Function,-,strtod,double,"const char*, char**" +Function,-,strtod_l,double,"const char*, char**, locale_t" +Function,+,strtof,float,"const char*, char**" +Function,-,strtof_l,float,"const char*, char**, locale_t" +Function,-,strtok,char*,"char*, const char*" +Function,-,strtok_r,char*,"char*, const char*, char**" +Function,+,strtol,long,"const char*, char**, int" +Function,-,strtol_l,long,"const char*, char**, int, locale_t" +Function,-,strtold,long double,"const char*, char**" +Function,-,strtold_l,long double,"const char*, char**, locale_t" +Function,-,strtoll,long long,"const char*, char**, int" +Function,-,strtoll_l,long long,"const char*, char**, int, locale_t" +Function,+,strtoul,unsigned long,"const char*, char**, int" +Function,-,strtoul_l,unsigned long,"const char*, char**, int, locale_t" +Function,+,strtoull,unsigned long long,"const char*, char**, int" +Function,-,strtoull_l,unsigned long long,"const char*, char**, int, locale_t" +Function,-,strupr,char*,char* +Function,-,strverscmp,int,"const char*, const char*" +Function,-,strxfrm,size_t,"char*, const char*, size_t" +Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" +Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_alloc,Submenu*, +Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_reset,void,Submenu* +Function,+,submenu_set_header,void,"Submenu*, const char*" +Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" +Function,-,system,int,const char* +Function,+,tar_archive_add_dir,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_add_file,_Bool,"TarArchive*, const char*, const char*, const int32_t" +Function,+,tar_archive_alloc,TarArchive*,Storage* +Function,+,tar_archive_dir_add_element,_Bool,"TarArchive*, const char*" +Function,+,tar_archive_file_add_data_block,_Bool,"TarArchive*, const uint8_t*, const int32_t" +Function,+,tar_archive_file_add_header,_Bool,"TarArchive*, const char*, const int32_t" +Function,+,tar_archive_file_finalize,_Bool,TarArchive* +Function,+,tar_archive_finalize,_Bool,TarArchive* +Function,+,tar_archive_free,void,TarArchive* +Function,+,tar_archive_get_entries_count,int32_t,TarArchive* +Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode" +Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*" +Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t" +Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter" +Function,-,tempnam,char*,"const char*, const char*" +Function,+,text_box_alloc,TextBox*, +Function,+,text_box_free,void,TextBox* +Function,+,text_box_get_view,View*,TextBox* +Function,+,text_box_reset,void,TextBox* +Function,+,text_box_set_focus,void,"TextBox*, TextBoxFocus" +Function,+,text_box_set_font,void,"TextBox*, TextBoxFont" +Function,+,text_box_set_text,void,"TextBox*, const char*" +Function,+,text_input_alloc,TextInput*, +Function,+,text_input_free,void,TextInput* +Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* +Function,+,text_input_get_validator_callback_context,void*,TextInput* +Function,+,text_input_get_view,View*,TextInput* +Function,+,text_input_reset,void,TextInput* +Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" +Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" +Function,-,time,time_t,time_t* +Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" +Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" +Function,-,tmpfile,FILE*, +Function,-,tmpnam,char*,char* +Function,-,toascii,int,int +Function,-,toascii_l,int,"int, locale_t" +Function,-,tolower,int,int +Function,-,tolower_l,int,"int, locale_t" +Function,-,toupper,int,int +Function,-,toupper_l,int,"int, locale_t" +Function,-,tzset,void, +Function,-,uECC_compress,void,"const uint8_t*, uint8_t*, uECC_Curve" +Function,+,uECC_compute_public_key,int,"const uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_curve_private_key_size,int,uECC_Curve +Function,-,uECC_curve_public_key_size,int,uECC_Curve +Function,-,uECC_decompress,void,"const uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_get_rng,uECC_RNG_Function, +Function,-,uECC_make_key,int,"uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_secp160r1,uECC_Curve, +Function,-,uECC_secp192r1,uECC_Curve, +Function,-,uECC_secp224r1,uECC_Curve, +Function,-,uECC_secp256k1,uECC_Curve, +Function,+,uECC_secp256r1,uECC_Curve, +Function,+,uECC_set_rng,void,uECC_RNG_Function +Function,-,uECC_shared_secret,int,"const uint8_t*, const uint8_t*, uint8_t*, uECC_Curve" +Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uECC_Curve" +Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" +Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" +Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" +Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" +Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ungetc,int,"int, FILE*" +Function,-,unsetenv,int,const char* +Function,-,usbd_poll,void,usbd_device* +Function,-,utoa,char*,"unsigned, char*, int" +Function,-,uxListRemove,UBaseType_t,ListItem_t* +Function,-,uxTaskGetNumberOfTasks,UBaseType_t, +Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t +Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t +Function,-,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t +Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t +Function,-,uxTaskResetEventItemValue,TickType_t, +Function,-,uxTimerGetReloadMode,UBaseType_t,TimerHandle_t +Function,-,uxTimerGetTimerNumber,UBaseType_t,TimerHandle_t +Function,-,vApplicationGetIdleTaskMemory,void,"StaticTask_t**, StackType_t**, uint32_t*" +Function,-,vApplicationGetTimerTaskMemory,void,"StaticTask_t**, StackType_t**, uint32_t*" +Function,-,vListInitialise,void,List_t* +Function,-,vListInitialiseItem,void,ListItem_t* +Function,-,vListInsert,void,"List_t*, ListItem_t*" +Function,-,vListInsertEnd,void,"List_t*, ListItem_t*" +Function,-,vPortDefineHeapRegions,void,const HeapRegion_t* +Function,-,vPortEndScheduler,void, +Function,+,vPortEnterCritical,void, +Function,+,vPortExitCritical,void, +Function,-,vPortFree,void,void* +Function,-,vPortGetHeapStats,void,HeapStats_t* +Function,-,vPortInitialiseBlocks,void, +Function,-,vPortSuppressTicksAndSleep,void,TickType_t +Function,-,vTaskAllocateMPURegions,void,"TaskHandle_t, const MemoryRegion_t*" +Function,-,vTaskDelay,void,const TickType_t +Function,-,vTaskDelete,void,TaskHandle_t +Function,-,vTaskEndScheduler,void, +Function,-,vTaskGenericNotifyGiveFromISR,void,"TaskHandle_t, UBaseType_t, BaseType_t*" +Function,-,vTaskGetInfo,void,"TaskHandle_t, TaskStatus_t*, BaseType_t, eTaskState" +Function,-,vTaskGetRunTimeStats,void,char* +Function,-,vTaskInternalSetTimeOutState,void,TimeOut_t* +Function,-,vTaskList,void,char* +Function,-,vTaskMissedYield,void, +Function,-,vTaskPlaceOnEventList,void,"List_t*, const TickType_t" +Function,-,vTaskPlaceOnEventListRestricted,void,"List_t*, TickType_t, const BaseType_t" +Function,-,vTaskPlaceOnUnorderedEventList,void,"List_t*, const TickType_t, const TickType_t" +Function,-,vTaskPriorityDisinheritAfterTimeout,void,"const TaskHandle_t, UBaseType_t" +Function,+,vTaskPrioritySet,void,"TaskHandle_t, UBaseType_t" +Function,-,vTaskRemoveFromUnorderedEventList,void,"ListItem_t*, const TickType_t" +Function,-,vTaskResume,void,TaskHandle_t +Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" +Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" +Function,-,vTaskSetTimeOutState,void,TimeOut_t* +Function,-,vTaskStartScheduler,void, +Function,-,vTaskStepTick,void,TickType_t +Function,-,vTaskSuspend,void,TaskHandle_t +Function,-,vTaskSuspendAll,void, +Function,-,vTaskSwitchContext,void, +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" +Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" +Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" +Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" +Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" +Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,variable_item_get_context,void*,VariableItem* +Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* +Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" +Function,+,variable_item_list_alloc,VariableItemList*, +Function,+,variable_item_list_free,void,VariableItemList* +Function,+,variable_item_list_get_selected_item_index,uint8_t,VariableItemList* +Function,+,variable_item_list_get_view,View*,VariableItemList* +Function,+,variable_item_list_reset,void,VariableItemList* +Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, VariableItemListEnterCallback, void*" +Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" +Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" +Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vdiprintf,int,"int, const char*, __gnuc_va_list" +Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" +Function,+,version_get,const Version*, +Function,+,version_get_builddate,const char*,const Version* +Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_gitbranch,const char*,const Version* +Function,+,version_get_gitbranchnum,const char*,const Version* +Function,+,version_get_githash,const char*,const Version* +Function,+,version_get_target,uint8_t,const Version* +Function,+,version_get_version,const char*,const Version* +Function,-,vfiprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfiscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,+,view_alloc,View*, +Function,+,view_allocate_model,void,"View*, ViewModelType, size_t" +Function,+,view_commit_model,void,"View*, _Bool" +Function,+,view_dispatcher_add_view,void,"ViewDispatcher*, uint32_t, View*" +Function,+,view_dispatcher_alloc,ViewDispatcher*, +Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" +Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* +Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_run,void,ViewDispatcher* +Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_send_to_back,void,ViewDispatcher* +Function,+,view_dispatcher_send_to_front,void,ViewDispatcher* +Function,+,view_dispatcher_set_custom_event_callback,void,"ViewDispatcher*, ViewDispatcherCustomEventCallback" +Function,+,view_dispatcher_set_event_callback_context,void,"ViewDispatcher*, void*" +Function,+,view_dispatcher_set_navigation_event_callback,void,"ViewDispatcher*, ViewDispatcherNavigationEventCallback" +Function,+,view_dispatcher_set_tick_event_callback,void,"ViewDispatcher*, ViewDispatcherTickEventCallback, uint32_t" +Function,+,view_dispatcher_stop,void,ViewDispatcher* +Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_free,void,View* +Function,+,view_free_model,void,View* +Function,+,view_get_model,void*,View* +Function,+,view_port_alloc,ViewPort*, +Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" +Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" +Function,+,view_port_free,void,ViewPort* +Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* +Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" +Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_set_height,void,"ViewPort*, uint8_t" +Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" +Function,+,view_port_set_width,void,"ViewPort*, uint8_t" +Function,+,view_port_update,void,ViewPort* +Function,+,view_set_context,void,"View*, void*" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" +Function,+,view_set_enter_callback,void,"View*, ViewCallback" +Function,+,view_set_exit_callback,void,"View*, ViewCallback" +Function,+,view_set_input_callback,void,"View*, ViewInputCallback" +Function,+,view_set_orientation,void,"View*, ViewOrientation" +Function,+,view_set_previous_callback,void,"View*, ViewNavigationCallback" +Function,+,view_set_update_callback,void,"View*, ViewUpdateCallback" +Function,+,view_set_update_callback_context,void,"View*, void*" +Function,+,view_stack_add_view,void,"ViewStack*, View*" +Function,+,view_stack_alloc,ViewStack*, +Function,+,view_stack_free,void,ViewStack* +Function,+,view_stack_get_view,View*,ViewStack* +Function,+,view_stack_remove_view,void,"ViewStack*, View*" +Function,+,view_tie_icon_animation,void,"View*, IconAnimation*" +Function,-,viprintf,int,"const char*, __gnuc_va_list" +Function,-,viscanf,int,"const char*, __gnuc_va_list" +Function,-,vprintf,int,"const char*, __gnuc_va_list" +Function,-,vscanf,int,"const char*, __gnuc_va_list" +Function,-,vsiprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsiscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,vsniprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsnprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" +Function,-,wctomb,int,"char*, wchar_t" +Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_alloc,Widget*, +Function,+,widget_free,void,Widget* +Function,+,widget_get_view,View*,Widget* +Function,+,widget_reset,void,Widget* +Function,-,write_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" +Function,-,xPortGetFreeHeapSize,size_t, +Function,-,xPortGetMinimumEverFreeHeapSize,size_t, +Function,-,xPortStartScheduler,BaseType_t, +Function,-,xTaskAbortDelay,BaseType_t,TaskHandle_t +Function,-,xTaskCallApplicationTaskHook,BaseType_t,"TaskHandle_t, void*" +Function,-,xTaskCatchUpTicks,BaseType_t,TickType_t +Function,-,xTaskCheckForTimeOut,BaseType_t,"TimeOut_t*, TickType_t*" +Function,-,xTaskCreate,BaseType_t,"TaskFunction_t, const char*, const uint16_t, void*, UBaseType_t, TaskHandle_t*" +Function,-,xTaskCreateStatic,TaskHandle_t,"TaskFunction_t, const char*, const uint32_t, void*, UBaseType_t, StackType_t*, StaticTask_t*" +Function,-,xTaskDelayUntil,BaseType_t,"TickType_t*, const TickType_t" +Function,-,xTaskGenericNotify,BaseType_t,"TaskHandle_t, UBaseType_t, uint32_t, eNotifyAction, uint32_t*" +Function,-,xTaskGenericNotifyFromISR,BaseType_t,"TaskHandle_t, UBaseType_t, uint32_t, eNotifyAction, uint32_t*, BaseType_t*" +Function,-,xTaskGenericNotifyStateClear,BaseType_t,"TaskHandle_t, UBaseType_t" +Function,-,xTaskGenericNotifyWait,BaseType_t,"UBaseType_t, uint32_t, uint32_t, uint32_t*, TickType_t" +Function,-,xTaskGetCurrentTaskHandle,TaskHandle_t, +Function,+,xTaskGetHandle,TaskHandle_t,const char* +Function,-,xTaskGetIdleTaskHandle,TaskHandle_t, +Function,+,xTaskGetSchedulerState,BaseType_t, +Function,+,xTaskGetTickCount,TickType_t, +Function,-,xTaskGetTickCountFromISR,TickType_t, +Function,-,xTaskIncrementTick,BaseType_t, +Function,-,xTaskPriorityDisinherit,BaseType_t,const TaskHandle_t +Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t +Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* +Function,-,xTaskResumeAll,BaseType_t, +Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreateTimerTask,BaseType_t, +Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" +Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t +Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t +Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t +Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, +Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t +Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" +Function,-,xTimerPendFunctionCallFromISR,BaseType_t,"PendedFunction_t, void*, uint32_t, BaseType_t*" +Variable,-,AHBPrescTable,const uint32_t[16], +Variable,-,APBPrescTable,const uint32_t[8], +Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,-,MSIRangeTable,const uint32_t[16], +Variable,-,SmpsPrescalerTable,const uint32_t[4][6], +Variable,+,SystemCoreClock,uint32_t, +Variable,+,_ctype_,const char[], +Variable,-,_daylight,int, +Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_ptr,_reent*, +Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_nerr,int, +Variable,-,_timezone,long, +Variable,-,_tzname,char*[2], +Variable,+,cli_vcp,CliSession, +Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, +Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, +Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*, +Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, +Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, +Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,gpio_button_back,const GpioPin, +Variable,+,gpio_button_down,const GpioPin, +Variable,+,gpio_button_left,const GpioPin, +Variable,+,gpio_button_ok,const GpioPin, +Variable,+,gpio_button_right,const GpioPin, +Variable,+,gpio_button_up,const GpioPin, +Variable,+,gpio_display_cs,const GpioPin, +Variable,+,gpio_display_di,const GpioPin, +Variable,+,gpio_display_rst_n,const GpioPin, +Variable,+,gpio_ext_pa0,const GpioPin, +Variable,+,gpio_ext_pa1,const GpioPin, +Variable,+,gpio_ext_pa15,const GpioPin, +Variable,+,gpio_ext_pa2,const GpioPin, +Variable,+,gpio_ext_pa4,const GpioPin, +Variable,+,gpio_ext_pa5,const GpioPin, +Variable,+,gpio_ext_pa6,const GpioPin, +Variable,+,gpio_ext_pa7,const GpioPin, +Variable,+,gpio_ext_pb13,const GpioPin, +Variable,+,gpio_ext_pb2,const GpioPin, +Variable,+,gpio_ext_pb3,const GpioPin, +Variable,+,gpio_ext_pb4,const GpioPin, +Variable,+,gpio_ext_pb5,const GpioPin, +Variable,+,gpio_ext_pb9,const GpioPin, +Variable,+,gpio_ext_pc0,const GpioPin, +Variable,+,gpio_ext_pc1,const GpioPin, +Variable,+,gpio_ext_pc3,const GpioPin, +Variable,+,gpio_ext_pc4,const GpioPin, +Variable,+,gpio_ext_pc5,const GpioPin, +Variable,+,gpio_ext_pd0,const GpioPin, +Variable,+,gpio_ext_pe4,const GpioPin, +Variable,+,gpio_i2c_power_scl,const GpioPin, +Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, +Variable,+,gpio_sdcard_cd,const GpioPin, +Variable,+,gpio_sdcard_cs,const GpioPin, +Variable,+,gpio_speaker,const GpioPin, +Variable,+,gpio_spi_d_miso,const GpioPin, +Variable,+,gpio_spi_d_mosi,const GpioPin, +Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_usart_rx,const GpioPin, +Variable,+,gpio_usart_tx,const GpioPin, +Variable,+,gpio_usb_dm,const GpioPin, +Variable,+,gpio_usb_dp,const GpioPin, +Variable,+,ibutton_gpio,const GpioPin, +Variable,+,input_pins,const InputPin[], +Variable,+,input_pins_count,const size_t, +Variable,+,message_blink_set_color_blue,const NotificationMessage, +Variable,+,message_blink_set_color_cyan,const NotificationMessage, +Variable,+,message_blink_set_color_green,const NotificationMessage, +Variable,+,message_blink_set_color_magenta,const NotificationMessage, +Variable,+,message_blink_set_color_red,const NotificationMessage, +Variable,+,message_blink_set_color_white,const NotificationMessage, +Variable,+,message_blink_set_color_yellow,const NotificationMessage, +Variable,+,message_blink_start_10,const NotificationMessage, +Variable,+,message_blink_start_100,const NotificationMessage, +Variable,+,message_blink_stop,const NotificationMessage, +Variable,+,message_blue_0,const NotificationMessage, +Variable,+,message_blue_255,const NotificationMessage, +Variable,+,message_click,const NotificationMessage, +Variable,+,message_delay_1,const NotificationMessage, +Variable,+,message_delay_10,const NotificationMessage, +Variable,+,message_delay_100,const NotificationMessage, +Variable,+,message_delay_1000,const NotificationMessage, +Variable,+,message_delay_25,const NotificationMessage, +Variable,+,message_delay_250,const NotificationMessage, +Variable,+,message_delay_50,const NotificationMessage, +Variable,+,message_delay_500,const NotificationMessage, +Variable,+,message_display_backlight_enforce_auto,const NotificationMessage, +Variable,+,message_display_backlight_enforce_on,const NotificationMessage, +Variable,+,message_display_backlight_off,const NotificationMessage, +Variable,+,message_display_backlight_on,const NotificationMessage, +Variable,+,message_do_not_reset,const NotificationMessage, +Variable,+,message_force_display_brightness_setting_1f,const NotificationMessage, +Variable,+,message_force_speaker_volume_setting_1f,const NotificationMessage, +Variable,+,message_force_vibro_setting_off,const NotificationMessage, +Variable,+,message_force_vibro_setting_on,const NotificationMessage, +Variable,+,message_green_0,const NotificationMessage, +Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_note_a0,const NotificationMessage, +Variable,+,message_note_a1,const NotificationMessage, +Variable,+,message_note_a2,const NotificationMessage, +Variable,+,message_note_a3,const NotificationMessage, +Variable,+,message_note_a4,const NotificationMessage, +Variable,+,message_note_a5,const NotificationMessage, +Variable,+,message_note_a6,const NotificationMessage, +Variable,+,message_note_a7,const NotificationMessage, +Variable,+,message_note_a8,const NotificationMessage, +Variable,+,message_note_as0,const NotificationMessage, +Variable,+,message_note_as1,const NotificationMessage, +Variable,+,message_note_as2,const NotificationMessage, +Variable,+,message_note_as3,const NotificationMessage, +Variable,+,message_note_as4,const NotificationMessage, +Variable,+,message_note_as5,const NotificationMessage, +Variable,+,message_note_as6,const NotificationMessage, +Variable,+,message_note_as7,const NotificationMessage, +Variable,+,message_note_as8,const NotificationMessage, +Variable,+,message_note_b0,const NotificationMessage, +Variable,+,message_note_b1,const NotificationMessage, +Variable,+,message_note_b2,const NotificationMessage, +Variable,+,message_note_b3,const NotificationMessage, +Variable,+,message_note_b4,const NotificationMessage, +Variable,+,message_note_b5,const NotificationMessage, +Variable,+,message_note_b6,const NotificationMessage, +Variable,+,message_note_b7,const NotificationMessage, +Variable,+,message_note_b8,const NotificationMessage, +Variable,+,message_note_c0,const NotificationMessage, +Variable,+,message_note_c1,const NotificationMessage, +Variable,+,message_note_c2,const NotificationMessage, +Variable,+,message_note_c3,const NotificationMessage, +Variable,+,message_note_c4,const NotificationMessage, +Variable,+,message_note_c5,const NotificationMessage, +Variable,+,message_note_c6,const NotificationMessage, +Variable,+,message_note_c7,const NotificationMessage, +Variable,+,message_note_c8,const NotificationMessage, +Variable,+,message_note_cs0,const NotificationMessage, +Variable,+,message_note_cs1,const NotificationMessage, +Variable,+,message_note_cs2,const NotificationMessage, +Variable,+,message_note_cs3,const NotificationMessage, +Variable,+,message_note_cs4,const NotificationMessage, +Variable,+,message_note_cs5,const NotificationMessage, +Variable,+,message_note_cs6,const NotificationMessage, +Variable,+,message_note_cs7,const NotificationMessage, +Variable,+,message_note_cs8,const NotificationMessage, +Variable,+,message_note_d0,const NotificationMessage, +Variable,+,message_note_d1,const NotificationMessage, +Variable,+,message_note_d2,const NotificationMessage, +Variable,+,message_note_d3,const NotificationMessage, +Variable,+,message_note_d4,const NotificationMessage, +Variable,+,message_note_d5,const NotificationMessage, +Variable,+,message_note_d6,const NotificationMessage, +Variable,+,message_note_d7,const NotificationMessage, +Variable,+,message_note_d8,const NotificationMessage, +Variable,+,message_note_ds0,const NotificationMessage, +Variable,+,message_note_ds1,const NotificationMessage, +Variable,+,message_note_ds2,const NotificationMessage, +Variable,+,message_note_ds3,const NotificationMessage, +Variable,+,message_note_ds4,const NotificationMessage, +Variable,+,message_note_ds5,const NotificationMessage, +Variable,+,message_note_ds6,const NotificationMessage, +Variable,+,message_note_ds7,const NotificationMessage, +Variable,+,message_note_ds8,const NotificationMessage, +Variable,+,message_note_e0,const NotificationMessage, +Variable,+,message_note_e1,const NotificationMessage, +Variable,+,message_note_e2,const NotificationMessage, +Variable,+,message_note_e3,const NotificationMessage, +Variable,+,message_note_e4,const NotificationMessage, +Variable,+,message_note_e5,const NotificationMessage, +Variable,+,message_note_e6,const NotificationMessage, +Variable,+,message_note_e7,const NotificationMessage, +Variable,+,message_note_e8,const NotificationMessage, +Variable,+,message_note_f0,const NotificationMessage, +Variable,+,message_note_f1,const NotificationMessage, +Variable,+,message_note_f2,const NotificationMessage, +Variable,+,message_note_f3,const NotificationMessage, +Variable,+,message_note_f4,const NotificationMessage, +Variable,+,message_note_f5,const NotificationMessage, +Variable,+,message_note_f6,const NotificationMessage, +Variable,+,message_note_f7,const NotificationMessage, +Variable,+,message_note_f8,const NotificationMessage, +Variable,+,message_note_fs0,const NotificationMessage, +Variable,+,message_note_fs1,const NotificationMessage, +Variable,+,message_note_fs2,const NotificationMessage, +Variable,+,message_note_fs3,const NotificationMessage, +Variable,+,message_note_fs4,const NotificationMessage, +Variable,+,message_note_fs5,const NotificationMessage, +Variable,+,message_note_fs6,const NotificationMessage, +Variable,+,message_note_fs7,const NotificationMessage, +Variable,+,message_note_fs8,const NotificationMessage, +Variable,+,message_note_g0,const NotificationMessage, +Variable,+,message_note_g1,const NotificationMessage, +Variable,+,message_note_g2,const NotificationMessage, +Variable,+,message_note_g3,const NotificationMessage, +Variable,+,message_note_g4,const NotificationMessage, +Variable,+,message_note_g5,const NotificationMessage, +Variable,+,message_note_g6,const NotificationMessage, +Variable,+,message_note_g7,const NotificationMessage, +Variable,+,message_note_g8,const NotificationMessage, +Variable,+,message_note_gs0,const NotificationMessage, +Variable,+,message_note_gs1,const NotificationMessage, +Variable,+,message_note_gs2,const NotificationMessage, +Variable,+,message_note_gs3,const NotificationMessage, +Variable,+,message_note_gs4,const NotificationMessage, +Variable,+,message_note_gs5,const NotificationMessage, +Variable,+,message_note_gs6,const NotificationMessage, +Variable,+,message_note_gs7,const NotificationMessage, +Variable,+,message_note_gs8,const NotificationMessage, +Variable,+,message_red_0,const NotificationMessage, +Variable,+,message_red_255,const NotificationMessage, +Variable,+,message_sound_off,const NotificationMessage, +Variable,+,message_vibro_off,const NotificationMessage, +Variable,+,message_vibro_on,const NotificationMessage, +Variable,+,periph_power,const GpioPin, +Variable,+,sequence_audiovisual_alert,const NotificationSequence, +Variable,+,sequence_blink_blue_10,const NotificationSequence, +Variable,+,sequence_blink_blue_100,const NotificationSequence, +Variable,+,sequence_blink_cyan_10,const NotificationSequence, +Variable,+,sequence_blink_cyan_100,const NotificationSequence, +Variable,+,sequence_blink_green_10,const NotificationSequence, +Variable,+,sequence_blink_green_100,const NotificationSequence, +Variable,+,sequence_blink_magenta_10,const NotificationSequence, +Variable,+,sequence_blink_magenta_100,const NotificationSequence, +Variable,+,sequence_blink_red_10,const NotificationSequence, +Variable,+,sequence_blink_red_100,const NotificationSequence, +Variable,+,sequence_blink_start_blue,const NotificationSequence, +Variable,+,sequence_blink_start_cyan,const NotificationSequence, +Variable,+,sequence_blink_start_green,const NotificationSequence, +Variable,+,sequence_blink_start_magenta,const NotificationSequence, +Variable,+,sequence_blink_start_red,const NotificationSequence, +Variable,+,sequence_blink_start_yellow,const NotificationSequence, +Variable,+,sequence_blink_stop,const NotificationSequence, +Variable,+,sequence_blink_white_100,const NotificationSequence, +Variable,+,sequence_blink_yellow_10,const NotificationSequence, +Variable,+,sequence_blink_yellow_100,const NotificationSequence, +Variable,+,sequence_charged,const NotificationSequence, +Variable,+,sequence_charging,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_auto,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_on,const NotificationSequence, +Variable,+,sequence_display_backlight_off,const NotificationSequence, +Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, +Variable,+,sequence_display_backlight_on,const NotificationSequence, +Variable,+,sequence_double_vibro,const NotificationSequence, +Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_not_charging,const NotificationSequence, +Variable,+,sequence_reset_blue,const NotificationSequence, +Variable,+,sequence_reset_display,const NotificationSequence, +Variable,+,sequence_reset_green,const NotificationSequence, +Variable,+,sequence_reset_red,const NotificationSequence, +Variable,+,sequence_reset_rgb,const NotificationSequence, +Variable,+,sequence_reset_sound,const NotificationSequence, +Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_set_blue_255,const NotificationSequence, +Variable,+,sequence_set_green_255,const NotificationSequence, +Variable,+,sequence_set_only_blue_255,const NotificationSequence, +Variable,+,sequence_set_only_green_255,const NotificationSequence, +Variable,+,sequence_set_only_red_255,const NotificationSequence, +Variable,+,sequence_set_red_255,const NotificationSequence, +Variable,+,sequence_set_vibro_on,const NotificationSequence, +Variable,+,sequence_single_vibro,const NotificationSequence, +Variable,+,sequence_solid_yellow,const NotificationSequence, +Variable,+,sequence_success,const NotificationSequence, +Variable,-,suboptarg,char*, +Variable,+,usb_cdc_dual,FuriHalUsbInterface, +Variable,+,usb_cdc_single,FuriHalUsbInterface, +Variable,+,usb_hid,FuriHalUsbInterface, +Variable,+,usb_hid_u2f,FuriHalUsbInterface, +Variable,+,usbd_devfs,const usbd_driver, +Variable,+,vibro_gpio,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c new file mode 100644 index 000000000..ad35a0074 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -0,0 +1,91 @@ +#include +#include + +#include + +#include + +#define TAG "FuriHal" + +void furi_hal_init_early() { + furi_hal_cortex_init_early(); + + furi_hal_clock_init_early(); + + furi_hal_resources_init_early(); + + furi_hal_os_init(); + + furi_hal_spi_config_init_early(); + + furi_hal_i2c_init_early(); + furi_hal_light_init(); + + furi_hal_rtc_init_early(); +} + +void furi_hal_deinit_early() { + furi_hal_rtc_deinit_early(); + + furi_hal_i2c_deinit_early(); + furi_hal_spi_config_deinit_early(); + + furi_hal_resources_deinit_early(); + + furi_hal_clock_deinit_early(); +} + +void furi_hal_init() { + furi_hal_mpu_init(); + furi_hal_clock_init(); + furi_hal_console_init(); + furi_hal_rtc_init(); + + furi_hal_interrupt_init(); + + furi_hal_flash_init(); + + furi_hal_resources_init(); + FURI_LOG_I(TAG, "GPIO OK"); + + furi_hal_version_init(); + + furi_hal_spi_config_init(); + + furi_hal_speaker_init(); + FURI_LOG_I(TAG, "Speaker OK"); + + furi_hal_crypto_init(); + + // USB +#ifndef FURI_RAM_EXEC + furi_hal_usb_init(); + FURI_LOG_I(TAG, "USB OK"); +#endif + + furi_hal_i2c_init(); + + // High Level + furi_hal_power_init(); + furi_hal_light_init(); +#ifndef FURI_RAM_EXEC + furi_hal_vibro_init(); +#endif + furi_hal_bt_init(); + furi_hal_compress_icon_init(); + + // FatFS driver initialization + MX_FATFS_Init(); + FURI_LOG_I(TAG, "FATFS OK"); +} + +void furi_hal_switch(void* address) { + __set_BASEPRI(0); + asm volatile("ldr r3, [%0] \n" + "msr msp, r3 \n" + "ldr r3, [%1] \n" + "mov pc, r3 \n" + : + : "r"(address), "r"(address + 0x4) + : "r3"); +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c new file mode 100644 index 000000000..dafeefdd0 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -0,0 +1,201 @@ +#include +#include + +#include +#include + +const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; +const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; + +const GpioPin gpio_display_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_display_rst_n = {.port = GPIOB, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_display_di = {.port = GPIOB, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_sdcard_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_sdcard_cd = {.port = GPIOC, .pin = LL_GPIO_PIN_10}; + +const GpioPin gpio_button_up = {.port = GPIOB, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_button_down = {.port = GPIOC, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_button_right = {.port = GPIOB, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_button_left = {.port = GPIOB, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_button_ok = {.port = GPIOH, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_spi_d_miso = {.port = GPIOC, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_spi_d_mosi = {.port = GPIOB, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_spi_d_sck = {.port = GPIOD, .pin = LL_GPIO_PIN_1}; + +const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_ext_pc5 = {.port = GPIOC, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pc4 = {.port = GPIOC, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa5 = {.port = GPIOA, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pb9 = {.port = GPIOB, .pin = LL_GPIO_PIN_9}; +const GpioPin gpio_ext_pa0 = {.port = GPIOA, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pa1 = {.port = GPIOA, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pa15 = {.port = GPIOA, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_ext_pe4 = {.port = GPIOE, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa2 = {.port = GPIOA, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb4 = {.port = GPIOB, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pb5 = {.port = GPIOB, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pd0 = {.port = GPIOD, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pb13 = {.port = GPIOB, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_usart_tx = {.port = GPIOB, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_usart_rx = {.port = GPIOB, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; + +const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; + +const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; + +const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; + +const GpioPinRecord gpio_pins[] = { + {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, + {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, + + {.pin = &gpio_ext_pc5, .name = "PC5", .debug = false}, + {.pin = &gpio_ext_pc4, .name = "PC4", .debug = false}, + {.pin = &gpio_ext_pa5, .name = "PA5", .debug = false}, + {.pin = &gpio_ext_pb9, .name = "PB9", .debug = false}, + {.pin = &gpio_ext_pa0, .name = "PA0", .debug = false}, + {.pin = &gpio_ext_pa1, .name = "PA1", .debug = false}, + {.pin = &gpio_ext_pa15, .name = "PA15", .debug = false}, + {.pin = &gpio_ext_pe4, .name = "PE4", .debug = false}, + {.pin = &gpio_ext_pa2, .name = "PA2", .debug = false}, + {.pin = &gpio_ext_pb4, .name = "PB4", .debug = false}, + {.pin = &gpio_ext_pb5, .name = "PB5", .debug = false}, + {.pin = &gpio_ext_pd0, .name = "PD0", .debug = false}, + {.pin = &gpio_ext_pb13, .name = "PB13", .debug = false}, + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, + {.pin = &gpio_speaker, .name = "PB8", .debug = true}, +}; + +const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); + +const InputPin input_pins[] = { + {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, + {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, + {.gpio = &gpio_button_right, .key = InputKeyRight, .inverted = true, .name = "Right"}, + {.gpio = &gpio_button_left, .key = InputKeyLeft, .inverted = true, .name = "Left"}, + {.gpio = &gpio_button_ok, .key = InputKeyOk, .inverted = false, .name = "OK"}, + {.gpio = &gpio_button_back, .key = InputKeyBack, .inverted = true, .name = "Back"}, +}; + +const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); + +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + +void furi_hal_resources_init_early() { + furi_hal_resources_init_input_pins(GpioModeInput); + + // SD Card stepdown control + furi_hal_gpio_write(&periph_power, 1); + furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + // Display pins + furi_hal_gpio_write(&gpio_display_rst_n, 1); + furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); + furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + // Pullup display reset pin for shutdown + SET_BIT(PWR->PUCRB, gpio_display_rst_n.pin); + CLEAR_BIT(PWR->PDCRB, gpio_display_rst_n.pin); + SET_BIT(PWR->CR3, PWR_CR3_APC); + + // Hard reset USB + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + furi_delay_us(5); // Device Driven disconnect: 2.5us + extra to compensate cables + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeAnalog); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + + // External header pins + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); +} + +void furi_hal_resources_init() { + // Button pins + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + + // Display pins + furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_display_rst_n, 0); + + furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_display_di, 0); + + // SD pins + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); + + furi_hal_gpio_init(&vibro_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI0_IRQn); + + NVIC_SetPriority(EXTI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI1_IRQn); + + NVIC_SetPriority(EXTI2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI2_IRQn); + + NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI3_IRQn); + + NVIC_SetPriority(EXTI4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI4_IRQn); + + NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI9_5_IRQn); + + NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI15_10_IRQn); +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h new file mode 100644 index 000000000..ef2cdae7f --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Input Related Constants */ +#define INPUT_DEBOUNCE_TICKS 4 + +/* Input Keys */ +typedef enum { + InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack, + InputKeyMAX, /**< Special value */ +} InputKey; + +/* Light */ +typedef enum { + LightRed = (1 << 0), + LightGreen = (1 << 1), + LightBlue = (1 << 2), + LightBacklight = (1 << 3), +} Light; + +typedef struct { + const GpioPin* gpio; + const InputKey key; + const bool inverted; + const char* name; +} InputPin; + +typedef struct { + const GpioPin* pin; + const char* name; + const bool debug; +} GpioPinRecord; + +extern const InputPin input_pins[]; +extern const size_t input_pins_count; + +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + +extern const GpioPin vibro_gpio; +extern const GpioPin ibutton_gpio; + +extern const GpioPin gpio_display_cs; +extern const GpioPin gpio_display_rst_n; +extern const GpioPin gpio_display_di; +extern const GpioPin gpio_sdcard_cs; +extern const GpioPin gpio_sdcard_cd; + +extern const GpioPin gpio_button_up; +extern const GpioPin gpio_button_down; +extern const GpioPin gpio_button_right; +extern const GpioPin gpio_button_left; +extern const GpioPin gpio_button_ok; +extern const GpioPin gpio_button_back; + +extern const GpioPin gpio_spi_d_miso; +extern const GpioPin gpio_spi_d_mosi; +extern const GpioPin gpio_spi_d_sck; + +extern const GpioPin gpio_ext_pc0; +extern const GpioPin gpio_ext_pc1; +extern const GpioPin gpio_ext_pc3; +extern const GpioPin gpio_ext_pb2; +extern const GpioPin gpio_ext_pb3; +extern const GpioPin gpio_ext_pa4; +extern const GpioPin gpio_ext_pa6; +extern const GpioPin gpio_ext_pa7; + +extern const GpioPin gpio_ext_pc5; +extern const GpioPin gpio_ext_pc4; +extern const GpioPin gpio_ext_pa5; +extern const GpioPin gpio_ext_pb9; +extern const GpioPin gpio_ext_pa0; +extern const GpioPin gpio_ext_pa1; +extern const GpioPin gpio_ext_pa15; +extern const GpioPin gpio_ext_pe4; +extern const GpioPin gpio_ext_pa2; +extern const GpioPin gpio_ext_pb4; +extern const GpioPin gpio_ext_pb5; +extern const GpioPin gpio_ext_pd0; +extern const GpioPin gpio_ext_pb13; + +extern const GpioPin gpio_usart_tx; +extern const GpioPin gpio_usart_rx; +extern const GpioPin gpio_i2c_power_sda; +extern const GpioPin gpio_i2c_power_scl; + +extern const GpioPin gpio_speaker; + +extern const GpioPin periph_power; + +extern const GpioPin gpio_usb_dm; +extern const GpioPin gpio_usb_dp; + +void furi_hal_resources_init_early(); + +void furi_hal_resources_deinit_early(); + +void furi_hal_resources_init(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f18/furi_hal/furi_hal_spi_config.c b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c new file mode 100644 index 000000000..0fbe55e2a --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include + +#define TAG "FuriHalSpiConfig" + +/* SPI Presets */ + +const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_2EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +/* SPI Buses */ + +FuriMutex* furi_hal_spi_bus_r_mutex = NULL; + +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_r_mutex); + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventDeactivate) { + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } +} + +FuriHalSpiBus furi_hal_spi_bus_r = { + .spi = SPI1, + .callback = furi_hal_spi_bus_r_event_callback, +}; + +FuriMutex* furi_hal_spi_bus_d_mutex = NULL; + +static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_d_mutex); + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventDeactivate) { + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } +} + +FuriHalSpiBus furi_hal_spi_bus_d = { + .spi = SPI2, + .callback = furi_hal_spi_bus_d_event_callback, +}; + +/* SPI Bus Handles */ + +inline static void furi_hal_spi_bus_r_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +inline static void furi_hal_spi_bus_nfc_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + // Configure GPIOs in normal SPI mode + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + // Configure GPIOs for st25r3916 Transparent mode + furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_write(handle->mosi, false); + furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_external_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_external_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + +inline static void furi_hal_spi_bus_d_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_display_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_display_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_display_cs, +}; + +static void furi_hal_spi_bus_handle_sd_fast_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; + +static void furi_hal_spi_bus_handle_sd_slow_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_spi_config.h b/firmware/targets/f18/furi_hal/furi_hal_spi_config.h new file mode 100644 index 000000000..da39fbfa6 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_spi_config.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Preset for ST25R916 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m; + +/** Preset for CC1101 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m; + +/** Preset for ST7567 (Display) */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m; + +/** Preset for SdCard in fast mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m; + +/** Preset for SdCard in slow mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m; + +/** Furi Hal Spi Bus R (External) */ +extern FuriHalSpiBus furi_hal_spi_bus_r; + +/** Furi Hal Spi Bus D (Display, SdCard) */ +extern FuriHalSpiBus furi_hal_spi_bus_d; + +/** External on `furi_hal_spi_bus_r` + * Preset: `furi_hal_spi_preset_1edge_low_2m` + * + * miso: pa6 + * mosi: pa7 + * sck: pb3 + * cs: pa4 (software controlled) + * + * @warning not initialized by default, call `furi_hal_spi_bus_handle_init` to initialize + * Bus pins are floating on inactive state, CS high after initialization + * + */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; + +/** ST7567(Display) on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; + +/** SdCard in fast mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; + +/** SdCard in slow mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f18/furi_hal/furi_hal_target_hw.h b/firmware/targets/f18/furi_hal/furi_hal_target_hw.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_target_hw.h @@ -0,0 +1 @@ +#pragma once diff --git a/firmware/targets/f18/furi_hal/furi_hal_version_device.c b/firmware/targets/f18/furi_hal/furi_hal_version_device.c new file mode 100644 index 000000000..1b5090b93 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_version_device.c @@ -0,0 +1,21 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 18) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Komi"; +} + +const char* furi_hal_version_get_model_code() { + return "N/A"; +} + +const char* furi_hal_version_get_fcc_id() { + return "N/A"; +} + +const char* furi_hal_version_get_ic_id() { + return "N/A"; +} diff --git a/firmware/targets/f18/target.json b/firmware/targets/f18/target.json new file mode 100644 index 000000000..2c3b27ab1 --- /dev/null +++ b/firmware/targets/f18/target.json @@ -0,0 +1,55 @@ +{ + "inherit": "7", + "include_paths": [ + "furi_hal" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper18", + "furi", + "freertos", + "stm32cubewb", + "hwdrivers", + "fatfs", + "littlefs", + "flipperformat", + "toolbox", + "microtar", + "usb_stm32", + "appframe", + "assets", + "misc", + "flipper_application", + "flipperformat", + "toolbox", + "flipper18" + ], + "excluded_sources": [ + "furi_hal_infrared.c", + "furi_hal_nfc.c", + "furi_hal_rfid.c", + "furi_hal_subghz.c" + ], + "excluded_headers": [ + "furi_hal_infrared.h", + "furi_hal_nfc.h", + "furi_hal_rfid.h", + "furi_hal_subghz.h", + "furi_hal_ibutton.h", + "furi_hal_subghz_configs.h" + ], + "excluded_modules": [ + "one_wire", + "nfc", + "lfrfid", + "subghz", + "infrared", + "st25rfal002" + ] +} \ No newline at end of file diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 14036ef1a..c1a979859 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.10,, +Version,+,12.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -41,14 +41,19 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_ibutton.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_nfc.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_target_hw.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, @@ -61,22 +66,18 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_i2c.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_ibutton.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_info.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_infrared.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_light.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_memory.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_mpu.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_nfc.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_power.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_random.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_region.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_rfid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_rtc.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_subghz.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, @@ -150,6 +151,16 @@ Header,+,lib/libusb_stm32/inc/usbd_core.h,, Header,+,lib/mbedtls/include/mbedtls/des.h,, Header,+,lib/mbedtls/include/mbedtls/sha1.h,, Header,+,lib/micro-ecc/uECC.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, @@ -852,6 +863,7 @@ Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" @@ -1022,7 +1034,7 @@ Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" -Function,+,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_deinit_early,void, Function,-,furi_hal_clock_init,void, Function,-,furi_hal_clock_init_early,void, Function,+,furi_hal_clock_mco_disable,void, @@ -1103,7 +1115,7 @@ Function,+,furi_hal_hid_u2f_is_connected,_Bool, Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" @@ -1241,7 +1253,7 @@ Function,-,furi_hal_region_init,void, Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* -Function,+,furi_hal_resources_deinit_early,void, +Function,-,furi_hal_resources_deinit_early,void, Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1270,7 +1282,7 @@ Function,+,furi_hal_rfid_tim_read_start,void, Function,+,furi_hal_rfid_tim_read_stop,void, Function,+,furi_hal_rfid_tim_reset,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* -Function,+,furi_hal_rtc_deinit_early,void, +Function,-,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_get_fault_data,uint32_t, @@ -1314,9 +1326,9 @@ Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_deinit_early,void, -Function,-,furi_hal_spi_init,void, -Function,+,furi_hal_spi_init_early,void, +Function,-,furi_hal_spi_config_deinit_early,void, +Function,-,furi_hal_spi_config_init,void, +Function,-,furi_hal_spi_config_init_early,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1370,6 +1382,7 @@ Function,+,furi_hal_version_do_i_belong_here,_Bool, Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, Function,+,furi_hal_version_get_ble_mac,const uint8_t*, Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, Function,+,furi_hal_version_get_firmware_version,const Version*, Function,+,furi_hal_version_get_hw_body,uint8_t, Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, @@ -1380,6 +1393,8 @@ Function,+,furi_hal_version_get_hw_region_name,const char*, Function,+,furi_hal_version_get_hw_target,uint8_t, Function,+,furi_hal_version_get_hw_timestamp,uint32_t, Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, Function,+,furi_hal_version_get_model_name,const char*, Function,+,furi_hal_version_get_name_ptr,const char*, Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, @@ -3026,6 +3041,8 @@ Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, Variable,+,gpio_rfid_carrier,const GpioPin, Variable,+,gpio_rfid_carrier_out,const GpioPin, diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index ec82c377a..7f2f5759f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -17,7 +17,7 @@ void furi_hal_init_early() { furi_hal_os_init(); - furi_hal_spi_init_early(); + furi_hal_spi_config_init_early(); furi_hal_i2c_init_early(); furi_hal_light_init(); @@ -29,7 +29,7 @@ void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); furi_hal_i2c_deinit_early(); - furi_hal_spi_deinit_early(); + furi_hal_spi_config_deinit_early(); furi_hal_resources_deinit_early(); @@ -52,7 +52,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_region_init(); - furi_hal_spi_init(); + furi_hal_spi_config_init(); furi_hal_ibutton_init(); FURI_LOG_I(TAG, "iButton OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index ab3855f42..8259be2f6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,5 +1,5 @@ -#include "furi_hal_bt_hid.h" -#include "furi_hal_usb_hid.h" +#include +#include #include "usb_hid.h" #include "dev_info_service.h" #include "battery_service.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index aa09dde52..2539e6bd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,4 +1,4 @@ -#include "furi_hal_bt_serial.h" +#include #include "dev_info_service.h" #include "battery_service.h" #include "serial_service.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index 192b83ee4..d0bce5038 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -1,4 +1,4 @@ -#include "furi_hal_cortex.h" +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c index 678eb2965..afc4fdf52 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -1,4 +1,4 @@ -#include "furi_hal_i2c_config.h" +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_ibutton.h b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h similarity index 98% rename from firmware/targets/furi_hal_include/furi_hal_ibutton.h rename to firmware/targets/f7/furi_hal/furi_hal_ibutton.h index 84ef0cd6a..fb57d628b 100644 --- a/firmware/targets/furi_hal_include/furi_hal_ibutton.h +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h @@ -7,7 +7,7 @@ #include #include -#include "furi_hal_gpio.h" +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index 442ae715d..dea1112ab 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -1,4 +1,4 @@ -#include "furi_hal_infrared.h" +#include #include #include "stm32wbxx_ll_dma.h" #include "sys/_stdint.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index 038ae9489..1b1132d0c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -1,5 +1,5 @@ -#include "furi_hal_interrupt.h" -#include "furi_hal_os.h" +#include +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index e6b3ab7d9..83e1603b7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -1,5 +1,5 @@ #include -#include "furi_hal_resources.h" +#include #include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 6381d1a91..ce81fd058 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,5 +1,5 @@ #include -#include "furi_hal_nfc.h" +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_nfc.h rename to firmware/targets/f7/furi_hal/furi_hal_nfc.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index e47f752ab..8f84b5fd8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -1,4 +1,4 @@ -#include "furi_hal_pwm.h" +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index cd019c0d9..f36407cc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -1,4 +1,4 @@ -#include "furi_hal_random.h" +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 4a32d7087..03cc6e714 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -62,6 +62,23 @@ const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; +const GpioPinRecord gpio_pins[] = { + {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, + {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, + {.pin = &gpio_speaker, .name = "PB8", .debug = true}, +}; + +const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); + const InputPin input_pins[] = { {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 64e5e19f9..51ea7bcc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -38,9 +38,18 @@ typedef struct { const char* name; } InputPin; +typedef struct { + const GpioPin* pin; + const char* name; + const bool debug; +} GpioPinRecord; + extern const InputPin input_pins[]; extern const size_t input_pins_count; +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + extern const GpioPin vibro_gpio; extern const GpioPin ibutton_gpio; diff --git a/firmware/targets/furi_hal_include/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_rfid.h rename to firmware/targets/f7/furi_hal/furi_hal_rfid.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_sd.c b/firmware/targets/f7/furi_hal/furi_hal_sd.c index 688a4e61b..1b0de5628 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_sd.c +++ b/firmware/targets/f7/furi_hal/furi_hal_sd.c @@ -1,24 +1,21 @@ -#include "furi_hal_sd.h" +#include #include #include #include void hal_sd_detect_init(void) { // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_INPUT); - LL_GPIO_SetPinSpeed(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_SPEED_FREQ_LOW); - LL_GPIO_SetPinPull(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_PULL_UP); + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullUp, GpioSpeedLow); } void hal_sd_detect_set_low(void) { // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_OUTPUT); - LL_GPIO_SetPinOutputType(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_OUTPUT_OPENDRAIN); - LL_GPIO_ResetOutputPin(SD_CD_GPIO_Port, SD_CD_Pin); + furi_hal_gpio_init_simple(&gpio_sdcard_cd, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); } bool hal_sd_detect(void) { - bool result = !(LL_GPIO_IsInputPinSet(SD_CD_GPIO_Port, SD_CD_Pin)); + bool result = !furi_hal_gpio_read(&gpio_sdcard_cd); return result; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2d54278d6..2f9d87080 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,38 +1,14 @@ -#include "furi_hal_spi.h" -#include "furi_hal_resources.h" +#include +#include #include #include #include -#include #include #include #include -#define TAG "FuriHalSpi" - -void furi_hal_spi_init_early() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_d); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); -} - -void furi_hal_spi_deinit_early() { - furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); - furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); -} - -void furi_hal_spi_init() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_r); - - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); - - FURI_LOG_I(TAG, "Init OK"); -} - void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 56f67bbf8..9cf332dac 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -1,5 +1,9 @@ #include #include +#include +#include + +#define TAG "FuriHalSpiConfig" /* SPI Presets */ @@ -72,6 +76,27 @@ const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { FuriMutex* furi_hal_spi_bus_r_mutex = NULL; +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_r); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 3441fd965..9bde15c33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,5 +1,5 @@ -#include "furi_hal_subghz.h" -#include "furi_hal_subghz_configs.h" +#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_subghz.h rename to firmware/targets/f7/furi_hal/furi_hal_subghz.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_target_hw.h b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h new file mode 100644 index 000000000..128122f84 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index e740155f5..0038bd348 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -1,6 +1,6 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" +#include +#include +#include #include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c index 88deafdcc..4c4f727ba 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_cdc.h" +#include +#include +#include +#include #include #include "usb.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index fc1ce024c..a3bc84227 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" +#include +#include +#include +#include #include #include "usb.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c index c099aec8a..fe711512a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb_hid_u2f.h" -#include "furi_hal_usb.h" +#include +#include +#include +#include #include #include "usb.h" #include "usb_hid.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index b7827ac7f..859a8c39f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -195,14 +195,6 @@ void furi_hal_version_init() { FURI_LOG_I(TAG, "Init OK"); } -bool furi_hal_version_do_i_belong_here() { - return furi_hal_version_get_hw_target() == 7; -} - -const char* furi_hal_version_get_model_name() { - return "Flipper Zero"; -} - FuriHalVersionOtpVersion furi_hal_version_get_otp_version() { if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) { return FuriHalVersionOtpVersionEmpty; diff --git a/firmware/targets/f7/furi_hal/furi_hal_version_device.c b/firmware/targets/f7/furi_hal/furi_hal_version_device.c new file mode 100644 index 000000000..c059c2cbe --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_version_device.c @@ -0,0 +1,21 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 7) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Zero"; +} + +const char* furi_hal_version_get_model_code() { + return "FZ.1"; +} + +const char* furi_hal_version_get_fcc_id() { + return "2A2V6-FZ"; +} + +const char* furi_hal_version_get_ic_id() { + return "27624-FZ"; +} diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/firmware/targets/f7/inc/FreeRTOSConfig.h similarity index 100% rename from firmware/targets/f7/Inc/FreeRTOSConfig.h rename to firmware/targets/f7/inc/FreeRTOSConfig.h diff --git a/firmware/targets/f7/Inc/alt_boot.h b/firmware/targets/f7/inc/alt_boot.h similarity index 100% rename from firmware/targets/f7/Inc/alt_boot.h rename to firmware/targets/f7/inc/alt_boot.h diff --git a/firmware/targets/f7/Inc/stm32.h b/firmware/targets/f7/inc/stm32.h similarity index 100% rename from firmware/targets/f7/Inc/stm32.h rename to firmware/targets/f7/inc/stm32.h diff --git a/firmware/targets/f7/Inc/stm32_assert.h b/firmware/targets/f7/inc/stm32_assert.h similarity index 100% rename from firmware/targets/f7/Inc/stm32_assert.h rename to firmware/targets/f7/inc/stm32_assert.h diff --git a/firmware/targets/f7/Src/dfu.c b/firmware/targets/f7/src/dfu.c similarity index 100% rename from firmware/targets/f7/Src/dfu.c rename to firmware/targets/f7/src/dfu.c diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/src/main.c similarity index 100% rename from firmware/targets/f7/Src/main.c rename to firmware/targets/f7/src/main.c diff --git a/firmware/targets/f7/Src/recovery.c b/firmware/targets/f7/src/recovery.c similarity index 99% rename from firmware/targets/f7/Src/recovery.c rename to firmware/targets/f7/src/recovery.c index fa57482ca..d56be4fe0 100644 --- a/firmware/targets/f7/Src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -51,4 +51,4 @@ void flipper_boot_recovery_exec() { furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } -} \ No newline at end of file +} diff --git a/firmware/targets/f7/Src/system_stm32wbxx.c b/firmware/targets/f7/src/system_stm32wbxx.c similarity index 100% rename from firmware/targets/f7/Src/system_stm32wbxx.c rename to firmware/targets/f7/src/system_stm32wbxx.c diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/src/update.c similarity index 99% rename from firmware/targets/f7/Src/update.c rename to firmware/targets/f7/src/update.c index a68a8b7a7..b223a5dc3 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/src/update.c @@ -42,7 +42,7 @@ static bool flipper_update_init() { furi_hal_rtc_init(); furi_hal_interrupt_init(); - furi_hal_spi_init(); + furi_hal_spi_config_init(); MX_FATFS_Init(); if(!hal_sd_detect()) { diff --git a/firmware/targets/f7/stm32wb55xx_flash.ld b/firmware/targets/f7/stm32wb55xx_flash.ld index e1fb98b9f..df4c5b726 100644 --- a/firmware/targets/f7/stm32wb55xx_flash.ld +++ b/firmware/targets/f7/stm32wb55xx_flash.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_flash.ld ** ** Abstract : System Workbench Minimal System calls file ** diff --git a/firmware/targets/f7/stm32wb55xx_ram_fw.ld b/firmware/targets/f7/stm32wb55xx_ram_fw.ld index db9e407c9..0ac9be4df 100644 --- a/firmware/targets/f7/stm32wb55xx_ram_fw.ld +++ b/firmware/targets/f7/stm32wb55xx_ram_fw.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_ram_fw.ld ** ** Abstract : System Workbench Minimal System calls file ** diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json new file mode 100644 index 000000000..49aa109bd --- /dev/null +++ b/firmware/targets/f7/target.json @@ -0,0 +1,45 @@ +{ + "include_paths": [ + "ble_glue", + "fatfs", + "furi_hal", + "inc" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "startup_script": "startup_stm32wb55xx_cm4.s", + "linker_script_flash": "stm32wb55xx_flash.ld", + "linker_script_ram": "stm32wb55xx_ram_fw.ld", + "linker_script_app": "application_ext.ld", + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper7", + "furi", + "freertos", + "stm32cubewb", + "hwdrivers", + "fatfs", + "littlefs", + "subghz", + "flipperformat", + "toolbox", + "nfc", + "microtar", + "usb_stm32", + "st25rfal002", + "infrared", + "appframe", + "assets", + "one_wire", + "misc", + "mbedtls", + "lfrfid", + "flipper_application", + "flipperformat", + "toolbox" + ] +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 8613c4d5e..ad4340dd4 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -10,37 +10,34 @@ template struct STOP_EXTERNING_ME {}; #endif -#include "furi_hal_cortex.h" -#include "furi_hal_clock.h" -#include "furi_hal_crypto.h" -#include "furi_hal_console.h" -#include "furi_hal_debug.h" -#include "furi_hal_os.h" -#include "furi_hal_sd.h" -#include "furi_hal_i2c.h" -#include "furi_hal_resources.h" -#include "furi_hal_region.h" -#include "furi_hal_rtc.h" -#include "furi_hal_speaker.h" -#include "furi_hal_gpio.h" -#include "furi_hal_light.h" -#include "furi_hal_power.h" -#include "furi_hal_interrupt.h" -#include "furi_hal_version.h" -#include "furi_hal_bt.h" -#include "furi_hal_spi.h" -#include "furi_hal_flash.h" -#include "furi_hal_subghz.h" -#include "furi_hal_vibro.h" -#include "furi_hal_ibutton.h" -#include "furi_hal_rfid.h" -#include "furi_hal_nfc.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" -#include "furi_hal_compress.h" -#include "furi_hal_uart.h" -#include "furi_hal_info.h" -#include "furi_hal_random.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 800fc3fe3..196b2edb3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -12,7 +12,7 @@ #include #include -#include "furi_hal_bt_serial.h" +#include #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) #define FURI_HAL_BT_STACK_VERSION_MINOR (12) diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index df7ffa93d..ab00ef0d7 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -8,13 +8,13 @@ extern "C" { #endif /** Early initialize SPI HAL */ -void furi_hal_spi_init_early(); +void furi_hal_spi_config_init_early(); /** Early deinitialize SPI HAL */ -void furi_hal_spi_deinit_early(); +void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ -void furi_hal_spi_init(); +void furi_hal_spi_config_init(); /** Initialize SPI Bus * diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 720fdfd17..aec4fc787 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -67,6 +67,24 @@ bool furi_hal_version_do_i_belong_here(); */ const char* furi_hal_version_get_model_name(); +/** Get model name + * + * @return model code C-string + */ +const char* furi_hal_version_get_model_code(); + +/** Get FCC ID + * + * @return FCC id as C-string + */ +const char* furi_hal_version_get_fcc_id(); + +/** Get IC id + * + * @return IC id as C-string + */ +const char* furi_hal_version_get_ic_id(); + /** Get OTP version * * @return OTP Version diff --git a/lib/SConscript b/lib/SConscript index abede5f33..d1e8e8c7e 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -8,7 +8,6 @@ env.Append( Dir("flipper_format"), Dir("infrared"), Dir("nfc"), - Dir("one_wire"), Dir("ST25RFAL002"), Dir("subghz"), Dir("toolbox"), @@ -16,16 +15,9 @@ env.Append( Dir("update_util"), Dir("print"), ], - SDK_HEADERS=[ - File("one_wire/one_wire_host_timing.h"), - File("one_wire/one_wire_host.h"), - File("one_wire/one_wire_slave.h"), - File("one_wire/one_wire_device.h"), - File("one_wire/ibutton/ibutton_worker.h"), - File("one_wire/maxim_crc.h"), - ], ) + env.Append( CPPPATH=[ "#/", @@ -34,6 +26,23 @@ env.Append( # Ugly hack Dir("../assets/compiled"), ], + SDK_HEADERS=[ + *( + File(f"#/lib/mlib/m-{name}.h") + for name in ( + "algo", + "array", + "bptree", + "core", + "deque", + "dict", + "list", + "rbtree", + "tuple", + "variant", + ) + ), + ], CPPDEFINES=[ '"M_MEMORY_FULL(x)=abort()"', ], @@ -47,13 +56,13 @@ env.Append( # littlefs # subghz # toolbox +# one_wire +# micro-ecc # misc # digital_signal -# fnv1a-hash -# micro-ecc +# fnv1a_hash # microtar # nfc -# one_wire # qrcode # u8g2 # update_util @@ -77,6 +86,7 @@ libs = env.BuildModules( "drivers", "fatfs", "flipper_format", + "one_wire", "infrared", "littlefs", "mbedtls", diff --git a/lib/app-scened-template/view_modules/popup_vm.cpp b/lib/app-scened-template/view_modules/popup_vm.cpp index e2c8732e8..330aa44ca 100644 --- a/lib/app-scened-template/view_modules/popup_vm.cpp +++ b/lib/app-scened-template/view_modules/popup_vm.cpp @@ -1,5 +1,6 @@ #include "popup_vm.h" -#include "gui/modules/popup.h" +#include + PopupVM::PopupVM() { popup = popup_alloc(); } diff --git a/lib/flipper_application/application_manifest.c b/lib/flipper_application/application_manifest.c index ab92e4930..fea92c262 100644 --- a/lib/flipper_application/application_manifest.c +++ b/lib/flipper_application/application_manifest.c @@ -1,5 +1,7 @@ #include "application_manifest.h" +#include + bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) { if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) || (manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) { @@ -19,3 +21,8 @@ bool flipper_application_manifest_is_compatible( return true; } + +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest) { + const Version* version = furi_hal_version_get_firmware_version(); + return version_get_target(version) == manifest->base.hardware_target_id; +} \ No newline at end of file diff --git a/lib/flipper_application/application_manifest.h b/lib/flipper_application/application_manifest.h index f46d44fd7..25e4f8d0a 100644 --- a/lib/flipper_application/application_manifest.h +++ b/lib/flipper_application/application_manifest.h @@ -65,6 +65,14 @@ bool flipper_application_manifest_is_compatible( const FlipperApplicationManifest* manifest, const ElfApiInterface* api_interface); +/** + * @brief Check if application is compatible with current hardware + * + * @param manifest + * @return bool + */ +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest); + #ifdef __cplusplus } #endif diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 8b049a7d4..58909218a 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -43,6 +43,10 @@ static FlipperApplicationPreloadStatus return FlipperApplicationPreloadStatusInvalidManifest; } + if(!flipper_application_manifest_is_target_compatible(&app->manifest)) { + return FlipperApplicationPreloadStatusTargetMismatch; + } + if(!flipper_application_manifest_is_compatible( &app->manifest, elf_file_get_api_interface(app->elf))) { return FlipperApplicationPreloadStatusApiMismatch; diff --git a/lib/misc.scons b/lib/misc.scons index 91ad276a0..cd2377ceb 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -26,7 +26,6 @@ sources = [] libs_recurse = [ "digital_signal", "micro-ecc", - "one_wire", "u8g2", "update_util", ] diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/parsers/all_in_one.c index c02710a29..edcc0d0c7 100644 --- a/lib/nfc/parsers/all_in_one.c +++ b/lib/nfc/parsers/all_in_one.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal.h" +#include #define ALL_IN_ONE_LAYOUT_UNKNOWN 0 #define ALL_IN_ONE_LAYOUT_A 1 diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c index e636bee00..aed41965c 100644 --- a/lib/nfc/parsers/plantain_4k_parser.c +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -3,7 +3,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext plantain_keys_4k[] = { {.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF}, diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c index c0e2a0947..3a1d17732 100644 --- a/lib/nfc/parsers/plantain_parser.c +++ b/lib/nfc/parsers/plantain_parser.c @@ -3,7 +3,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext plantain_keys[] = { {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c index 335248b2a..0e2ed5690 100644 --- a/lib/nfc/parsers/two_cities.c +++ b/lib/nfc/parsers/two_cities.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext two_cities_keys_4k[] = { {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index d642e290a..0e28c0074 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -3,7 +3,7 @@ #include "mifare_ultralight.h" #include "nfc_util.h" #include -#include "furi_hal_nfc.h" +#include #define TAG "MfUltralight" diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript new file mode 100644 index 000000000..5e06d21e3 --- /dev/null +++ b/lib/one_wire/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], + CPPPATH=[ + "#/lib/one_wire", + ], + SDK_HEADERS=[ + File("one_wire_host_timing.h"), + File("one_wire_host.h"), + File("one_wire_slave.h"), + File("one_wire_device.h"), + File("ibutton/ibutton_worker.h"), + File("maxim_crc.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="one_wire") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 1448f0ea6..d1c7dc356 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -5,8 +5,8 @@ #include #include -#include "furi.h" -#include "furi_hal.h" +#include +#include #include "../types.h" #ifdef __cplusplus diff --git a/lib/subghz/protocols/princeton_for_testing.c b/lib/subghz/protocols/princeton_for_testing.c index fa5616020..478d14cdf 100644 --- a/lib/subghz/protocols/princeton_for_testing.c +++ b/lib/subghz/protocols/princeton_for_testing.c @@ -1,6 +1,6 @@ #include "princeton_for_testing.h" -#include "furi_hal.h" +#include #include "../blocks/math.h" /* diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 57e23c38c..5d7ea0cec 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal_subghz_configs.h" +#include #define TAG "SubGhzSetting" diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index c6a9ccc5f..6e05b0233 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -21,8 +21,10 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", + [UpdatePrepareResultTargetMismatch] = "Hardware target mismatch", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", [UpdatePrepareResultIntFull] = "Need more free space in internal storage", + [UpdatePrepareResultUnspecifiedError] = "Unknown error", }; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { @@ -163,8 +165,9 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { result = UpdatePrepareResultOutdatedManifestVersion; break; } - - if(furi_hal_version_get_hw_target() != manifest->target) { + /* Only compare hardware target if it is set - pre-production devices accept any firmware*/ + if(furi_hal_version_get_hw_target() && + (furi_hal_version_get_hw_target() != manifest->target)) { result = UpdatePrepareResultTargetMismatch; break; } diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index a7460d889..aa03d265b 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Callable from enum import Enum import os @@ -71,6 +71,9 @@ class FlipperApplication: _appdir: Optional[object] = None _apppath: Optional[str] = None + def supports_hardware_target(self, target: str): + return target in self.targets or "all" in self.targets + class AppManager: def __init__(self): @@ -158,15 +161,26 @@ class AppBuildset: FlipperAppType.STARTUP, ) - def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str): + @staticmethod + def print_writer(message): + print(message) + + def __init__( + self, + appmgr: AppManager, + appnames: List[str], + hw_target: str, + message_writer: Callable = None, + ): self.appmgr = appmgr self.appnames = set(appnames) - self.hw_target = hw_target self._orig_appnames = appnames + self.hw_target = hw_target + self._writer = message_writer if message_writer else self.print_writer self._process_deps() - self._filter_by_target() self._check_conflicts() self._check_unsatisfied() # unneeded? + self._check_target_match() self.apps = sorted( list(map(self.appmgr.get, self.appnames)), key=lambda app: app.appid, @@ -175,28 +189,32 @@ class AppBuildset: def _is_missing_dep(self, dep_name: str): return dep_name not in self.appnames - def _filter_by_target(self): - for appname in self.appnames.copy(): - app = self.appmgr.get(appname) - # if app.apptype not in self.BUILTIN_APP_TYPES: - if not any(map(lambda t: t in app.targets, ["all", self.hw_target])): - print( - f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}" - ) - self.appnames.remove(appname) + def _check_if_app_target_supported(self, app_name: str): + return self.appmgr.get(app_name).supports_hardware_target(self.hw_target) + + def _get_app_depends(self, app_name: str) -> List[str]: + # Skip app if its target is not supported by the target we are building for + if not self._check_if_app_target_supported(app_name): + self._writer( + f"Skipping {app_name} due to target mismatch (building for {self.hw_target}, app supports {app_def.targets}" + ) + return [] + + app_def = self.appmgr.get(app_name) + return list( + filter( + self._check_if_app_target_supported, + filter(self._is_missing_dep, app_def.provides + app_def.requires), + ) + ) def _process_deps(self): while True: provided = [] - for app in self.appnames: - # print(app) - provided.extend( - filter( - self._is_missing_dep, - self.appmgr.get(app).provides + self.appmgr.get(app).requires, - ) - ) - # print("provides round", provided) + for app_name in self.appnames: + provided.extend(self._get_app_depends(app_name)) + + # print("provides round: ", provided) if len(provided) == 0: break self.appnames.update(provided) @@ -204,7 +222,6 @@ class AppBuildset: def _check_conflicts(self): conflicts = [] for app in self.appnames: - # print(app) if conflict_app_name := list( filter( lambda dep_name: dep_name in self.appnames, @@ -231,6 +248,17 @@ class AppBuildset: f"Unsatisfied dependencies for {', '.join(f'{missing_dep[0]}: {missing_dep[1]}' for missing_dep in unsatisfied)}" ) + def _check_target_match(self): + incompatible = [] + for app in self.appnames: + if not self.appmgr.get(app).supports_hardware_target(self.hw_target): + incompatible.append(app) + + if len(incompatible): + raise AppBuilderException( + f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}" + ) + def get_apps_cdefs(self): cdefs = set() for app in self.apps: diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 96528f4e5..9dbe30720 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -1,5 +1,6 @@ from SCons.Builder import Builder from SCons.Action import Action +from SCons.Errors import StopError from SCons.Warnings import warn, WarningOnByDefault from ansi.color import fg @@ -32,9 +33,13 @@ def LoadAppManifest(env, entry): def PrepareApplicationsBuild(env): - appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( - env["APPS"], env.subst("f${TARGET_HW}") - ) + try: + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + env["APPS"], env.subst("f${TARGET_HW}") + ) + except Exception as e: + raise StopError(e) + env.Append( SDK_HEADERS=appbuild.get_sdk_headers(), ) diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py new file mode 100644 index 000000000..b4e1e58ac --- /dev/null +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -0,0 +1,134 @@ +from SCons.Builder import Builder +from SCons.Action import Action +import json + + +class HardwareTargetLoader: + def __init__(self, env, target_scons_dir, target_id): + self.env = env + self.target_scons_dir = target_scons_dir + self.target_dir = self._getTargetDir(target_id) + # self.target_id = target_id + self.layered_target_dirs = [] + + self.include_paths = [] + self.sdk_header_paths = [] + self.startup_script = None + self.linker_script_flash = None + self.linker_script_ram = None + self.linker_script_app = None + self.sdk_symbols = None + self.linker_dependencies = [] + self.excluded_sources = [] + self.excluded_headers = [] + self.excluded_modules = [] + self._processTargetDefinitions(target_id) + + def _getTargetDir(self, target_id): + return self.target_scons_dir.Dir(f"f{target_id}") + + def _loadDescription(self, target_id): + target_json_file = self._getTargetDir(target_id).File("target.json") + if not target_json_file.exists(): + raise Exception(f"Target file {target_json_file} does not exist") + with open(target_json_file.get_abspath(), "r") as f: + vals = json.load(f) + return vals + + def _processTargetDefinitions(self, target_id): + self.layered_target_dirs.append(f"targets/f{target_id}") + + config = self._loadDescription(target_id) + + for path_list in ("include_paths", "sdk_header_paths"): + getattr(self, path_list).extend( + f"#/firmware/targets/f{target_id}/{p}" + for p in config.get(path_list, []) + ) + + self.excluded_sources.extend(config.get("excluded_sources", [])) + self.excluded_headers.extend(config.get("excluded_headers", [])) + self.excluded_modules.extend(config.get("excluded_modules", [])) + + file_attrs = ( + # (name, use_src_node) + ("startup_script", False), + ("linker_script_flash", True), + ("linker_script_ram", True), + ("linker_script_app", True), + ("sdk_symbols", True), + ) + + for attr_name, use_src_node in file_attrs: + if (val := config.get(attr_name)) and not getattr(self, attr_name): + node = self.env.File(f"firmware/targets/f{target_id}/{val}") + if use_src_node: + node = node.srcnode() + setattr(self, attr_name, node) + + for attr_name in ("linker_dependencies",): + if (val := config.get(attr_name)) and not getattr(self, attr_name): + setattr(self, attr_name, val) + + if inherited_target := config.get("inherit", None): + self._processTargetDefinitions(inherited_target) + + def gatherSources(self): + sources = [self.startup_script] + seen_filenames = set(self.excluded_sources) + # print("Layers: ", self.layered_target_dirs) + for target_dir in self.layered_target_dirs: + accepted_sources = list( + filter( + lambda f: f.name not in seen_filenames, + self.env.GlobRecursive("*.c", target_dir), + ) + ) + seen_filenames.update(f.name for f in accepted_sources) + sources.extend(accepted_sources) + # print(f"Found {len(sources)} sources: {list(f.name for f in sources)}") + return sources + + def gatherSdkHeaders(self): + sdk_headers = [] + seen_sdk_headers = set(self.excluded_headers) + for sdk_path in self.sdk_header_paths: + # dirty, but fast - exclude headers from overlayed targets by name + # proper way would be to use relative paths, but names will do for now + for header in self.env.GlobRecursive("*.h", sdk_path, "*_i.h"): + if header.name not in seen_sdk_headers: + seen_sdk_headers.add(header.name) + sdk_headers.append(header) + return sdk_headers + + +def ConfigureForTarget(env, target_id): + target_loader = HardwareTargetLoader(env, env.Dir("#/firmware/targets"), target_id) + env.Replace( + TARGET_CFG=target_loader, + SDK_DEFINITION=target_loader.sdk_symbols, + SKIP_MODULES=target_loader.excluded_modules, + ) + + env.Append( + CPPPATH=target_loader.include_paths, + SDK_HEADERS=target_loader.gatherSdkHeaders(), + ) + + +def ApplyLibFlags(env): + flags_to_apply = env["FW_LIB_OPTS"].get( + env.get("FW_LIB_NAME"), + env["FW_LIB_OPTS"]["Default"], + ) + # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) + env.MergeFlags(flags_to_apply) + + +def generate(env): + env.AddMethod(ConfigureForTarget) + env.AddMethod(ApplyLibFlags) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/sconsmodular.py b/scripts/fbt_tools/sconsmodular.py index b115706cb..57ae8f055 100644 --- a/scripts/fbt_tools/sconsmodular.py +++ b/scripts/fbt_tools/sconsmodular.py @@ -22,6 +22,8 @@ def BuildModule(env, module): def BuildModules(env, modules): result = [] for module in modules: + if module in env.get("SKIP_MODULES", []): + continue build_res = env.BuildModule(module) # print("module ", module, build_res) if build_res is None: diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e01c8a39e..e3ddc59aa 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -79,6 +79,7 @@ vars.AddVariables( default="7", allowed_values=[ "7", + "18", ], ), ( @@ -197,7 +198,7 @@ vars.AddVariables( # "basic_plugins", # Debug # "debug_apps", - ) + ), }, ), ( diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index bff9a8c30..abe1a4534 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -16,7 +16,7 @@ appenv = ENV["APPENV"] = ENV.Clone( ) appenv.Replace( - LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), + LINKER_SCRIPT_PATH=appenv["APP_LINKER_SCRIPT_PATH"], ) appenv.AppendUnique( @@ -78,17 +78,20 @@ known_extapps = [ if extra_app_list := GetOption("extra_ext_apps"): known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) +incompatible_apps = [] for app in known_extapps: - if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])): - warn( - WarningOnByDefault, - f"Can't build '{app.name}' (id '{app.appid}'): target mismatch" - f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}", - ) + if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): + incompatible_apps.append(app) continue appenv.BuildAppElf(app) +if incompatible_apps: + warn( + WarningOnByDefault, + f"Skipping build of {len(incompatible_apps)} incompatible app(s): " + + ", ".join(f"'{app.name}' (id '{app.appid}')" for app in incompatible_apps), + ) if appenv["FORCE"]: appenv.AlwaysBuild( diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index 9f707b4d8..e4cc8db58 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -49,18 +49,15 @@ ENV.AppendUnique( ], ) -ENV.SetDefault( - LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", -) if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", - LINKER_SCRIPT="stm32wb55xx_ram_fw", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_ram, ) else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", - LINKER_SCRIPT="stm32wb55xx_flash", - APP_LINKER_SCRIPT="application_ext", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_flash, + APP_LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_app, ) From ce17c0d55ef8596fb2f1ce45929d0085e501aa48 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:14:30 +0100 Subject: [PATCH 23/75] Update archive_browser_view.h --- applications/main/archive/views/archive_browser_view.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a7414fffe..a525a7db6 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -9,7 +9,10 @@ #include #include #include -#include +#include "../helpers/archive_files.h" +#include "../helpers/archive_menu.h" +#include "../helpers/archive_favorites.h" +#include "gui/modules/file_browser_worker.h" #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 From 189055d8f36e29ba93fa597a447be5804786f06a Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:44:13 +0100 Subject: [PATCH 24/75] fix bs --- applications/main/gpio/gpio_item.c | 51 +++++++++++++++++++++++++++++ applications/main/gpio/gpio_item.h | 15 +++++++++ firmware/targets/f7/api_symbols.csv | 12 +++---- 3 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 applications/main/gpio/gpio_item.c create mode 100644 applications/main/gpio/gpio_item.h diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c new file mode 100644 index 000000000..2d0f5f676 --- /dev/null +++ b/applications/main/gpio/gpio_item.c @@ -0,0 +1,51 @@ +#include "gpio_item.h" + +#include + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"1.2: PA7", &gpio_ext_pa7}, + {"1.3: PA6", &gpio_ext_pa6}, + {"1.4: PA4", &gpio_ext_pa4}, + {"1.5: PB3", &gpio_ext_pb3}, + {"1.6: PB2", &gpio_ext_pb2}, + {"1.7: PC3", &gpio_ext_pc3}, + {"2.7: PC1", &gpio_ext_pc1}, + {"2.8: PC0", &gpio_ext_pc0}, +}; + +void gpio_item_configure_pin(uint8_t index, GpioMode mode) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, false); + furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_item_configure_all_pins(GpioMode mode) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_configure_pin(i, mode); + } +} + +void gpio_item_set_pin(uint8_t index, bool level) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, level); +} + +void gpio_item_set_all_pins(bool level) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_set_pin(i, level); + } +} + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT + 1); + if(index == GPIO_ITEM_COUNT) { + return "ALL"; + } else { + return gpio_item[index].name; + } +} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h new file mode 100644 index 000000000..5cb2b86c1 --- /dev/null +++ b/applications/main/gpio/gpio_item.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define GPIO_ITEM_COUNT 8 + +void gpio_item_configure_pin(uint8_t index, GpioMode mode); + +void gpio_item_configure_all_pins(GpioMode mode); + +void gpio_item_set_pin(uint8_t index, bool level); + +void gpio_item_set_all_pins(bool level); + +const char* gpio_item_get_pin_name(uint8_t index); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index eccc33cbd..ef978eca9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.7,, +Version,+,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,7 +205,6 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, -Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2077,9 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" +Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, -Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 894fe99abd8181b6eee87d03392114211353c4b8 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:47:52 +0100 Subject: [PATCH 25/75] Revert "fix bs" This reverts commit 189055d8f36e29ba93fa597a447be5804786f06a. --- firmware/targets/f7/api_symbols.csv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ef978eca9..eccc33cbd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.8,, +Version,+,12.7,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,6 +205,7 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, +Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2076,10 +2077,9 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" -Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, -Function,-,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, +Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 0bc1a003685a242200c3fb4a009938d83105b643 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:48:30 +0100 Subject: [PATCH 26/75] Update api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index eccc33cbd..ebfeec172 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.7,, +Version,v,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,7 +205,6 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, -Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2077,9 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" +Function,?,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,?,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, -Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, +Function,?,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,?,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" +Function,?,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 15ec5b4e0dd3c7cac33a795d46899e21cc422971 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Wed, 8 Feb 2023 03:58:08 +0100 Subject: [PATCH 27/75] fixed api, fixed cartfiles & removed my stupidity --- applications/main/gpio/gpio_item.c | 51 ------------------ applications/main/gpio/gpio_item.h | 15 ------ .../main/gpio/views/gpio_i2c_scanner.c | 2 +- applications/main/gpio/views/gpio_i2c_sfp.c | 2 +- assets/resources/wav_player/CartKeyLock78.wav | Bin 365342 -> 61032 bytes .../resources/wav_player/CartKeyUnlock78.wav | Bin 396548 -> 66234 bytes firmware/targets/f7/api_symbols.csv | 12 ++--- 7 files changed, 8 insertions(+), 74 deletions(-) delete mode 100644 applications/main/gpio/gpio_item.c delete mode 100644 applications/main/gpio/gpio_item.h diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c deleted file mode 100644 index 2d0f5f676..000000000 --- a/applications/main/gpio/gpio_item.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "gpio_item.h" - -#include - -typedef struct { - const char* name; - const GpioPin* pin; -} GpioItem; - -static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { - {"1.2: PA7", &gpio_ext_pa7}, - {"1.3: PA6", &gpio_ext_pa6}, - {"1.4: PA4", &gpio_ext_pa4}, - {"1.5: PB3", &gpio_ext_pb3}, - {"1.6: PB2", &gpio_ext_pb2}, - {"1.7: PC3", &gpio_ext_pc3}, - {"2.7: PC1", &gpio_ext_pc1}, - {"2.8: PC0", &gpio_ext_pc0}, -}; - -void gpio_item_configure_pin(uint8_t index, GpioMode mode) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, false); - furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); -} - -void gpio_item_configure_all_pins(GpioMode mode) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_configure_pin(i, mode); - } -} - -void gpio_item_set_pin(uint8_t index, bool level) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, level); -} - -void gpio_item_set_all_pins(bool level) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_set_pin(i, level); - } -} - -const char* gpio_item_get_pin_name(uint8_t index) { - furi_assert(index < GPIO_ITEM_COUNT + 1); - if(index == GPIO_ITEM_COUNT) { - return "ALL"; - } else { - return gpio_item[index].name; - } -} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h deleted file mode 100644 index 5cb2b86c1..000000000 --- a/applications/main/gpio/gpio_item.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#define GPIO_ITEM_COUNT 8 - -void gpio_item_configure_pin(uint8_t index, GpioMode mode); - -void gpio_item_configure_all_pins(GpioMode mode); - -void gpio_item_set_pin(uint8_t index, bool level); - -void gpio_item_set_all_pins(bool level); - -const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/main/gpio/views/gpio_i2c_scanner.c b/applications/main/gpio/views/gpio_i2c_scanner.c index 0f00639d6..fc9ed78a7 100644 --- a/applications/main/gpio/views/gpio_i2c_scanner.c +++ b/applications/main/gpio/views/gpio_i2c_scanner.c @@ -1,6 +1,6 @@ #include #include "gpio_i2c_scanner.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include diff --git a/applications/main/gpio/views/gpio_i2c_sfp.c b/applications/main/gpio/views/gpio_i2c_sfp.c index 85492e783..c8e14ce7a 100644 --- a/applications/main/gpio/views/gpio_i2c_sfp.c +++ b/applications/main/gpio/views/gpio_i2c_sfp.c @@ -1,6 +1,6 @@ #include #include "gpio_i2c_sfp.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include diff --git a/assets/resources/wav_player/CartKeyLock78.wav b/assets/resources/wav_player/CartKeyLock78.wav index 71f56f328b076c708d621fa29c2cd93e022881f4..744f9d1a0f4f62944e6249d7a042cfc6ba9f6184 100644 GIT binary patch literal 61032 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 literal 365342 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

-ac3o0IeWc?XA;mG8fAYg16T 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 diff --git a/assets/resources/wav_player/CartKeyUnlock78.wav b/assets/resources/wav_player/CartKeyUnlock78.wav index 41bd420e96bf84ee8b3e3bc9c4e82c3406fad7ba..a82a45803783f74ab47779b83057d89fcbb4c651 100644 GIT binary patch literal 66234 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 R^&cPp{`cLVzwMld{{Xdg`{4ip literal 396548 zcmeFZXH=VKx;E;WNp_M+Z>|_)FvfID?_j`~jseqqM^u48y&@zeB#=OW=)L!Du<5-S zFufTU+%s{KnIvoG+w+YN_tIiHv(J2c@3YQ6=hyrEfQOe?Z@Ki8yIj}pYHMx%_fP)q zgy%Jn>k)CuGGClHapHF;et&{~_o#z|-iIf`f|G-{kItbZ4M6}w06_pj06_pj06_pj z06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj z06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj z06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj z06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj06_pj z06_pj06_pj06_pj06_pj072ltg#hEI{kKj;PC^hs5I_(>5I_(>5I_(>5I_(>5I_(> z5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(> z5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(> z5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(> z5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(> z5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(>5I_(> z5cnTOfJUR!8AlB+D(S!L!BN^#J3ixI_y6x_QK@u#>QQ4bs8kAtMoUdiOG{0qQpsd0 zH8qvRVlt^zB9TmHFeH5vi9&(nsi`Cq9#5gLSX^#e8j*;_l1MBTkH=yWiOI=CBJ|m8 zNuSN;^I0qcAvu{u;&AxDTz$Z$jHiKGLw@N6DgF;OrbC}H8C+Efl3t$v$Git zG&&)HPS4I33aQli_@pE@J2#il$6{k*@OYsxHy4X_cDA=ClgG#R_nVuYoxlB7TRS)R z@NjF3NR*K|cP=q;ZSCdDvN97BIk~8)`T5tc8yl}*mz50-U0nS2*W0&kY`*xy(z3n% z_3ObwSJ$ti%?x!l9U zM~|3Hd3hNb0%3dm@nbGmUjCbJLPIAfcXqO}R8>!%3JIB+dG@Tl+|cmUDIcGSi64LL z?{{^Tm34O?82JACkr5vs8JQb58XI4{n4S&~zj#q!KRbJ4V|+X&MqXZ9o5NXNo|r(R z<>ggWXtdSUtt~#^$Vf|zNL*ap*~!nhwA9p0PF`4e@}#Qj`t^$!-QC;Twzis@+}ti- zc5l{W3!Enw6*d0 zsi~C}K3`w|(xsrF?(T(!oE#IAix<7UySuly>*^dG<>XvlJ35{{8yky=P*rtssHu7U zcxEOcK~>eEM z8s_Fcc(AmD#VRUlXt3Ga+dDg4uAZKzCY8Ffvb~*|sjsi3l$10(yR(y%qpK?=a8pnyi-yZ7{I zdHJ<#3JTHD3k%)z&Y;0|%r70;%OGigfOx(Ypp028T z?p$c-=;-clL4lExv~*C=#Kh~@ot>_(ii!aNLqo4$4-9yD$;-RAbauXYF*4%kCoOGa zQdG3NJ2Da)DkGz-D->>QOiaYY$jV;5%H!?rZEi9c8b>WPbz@_1571LrCy`cHA3e&; z)6=_fAtYpYcz3s?#N1q3+RLk_XJ@CT#>Pfk8t(AqNnf9*=f#UQHud#~hm(_0QA$eJ zua}oUemp;)kf5NTtDBXzySuVNBwjgcINZ$4{5+Ygq$DSYMo&+#uk(0XS{E;(P%t;@ z>m3|aR3akA$6vhY=x}wta>d7|zyHMxcpn)VE31l%-Q7EP0s}8zgwe3IH8KL|X=!C; z?d`3ulF6#7SFbXe>+4%v85#QeSFcj3>+1&xFal&`!oo&IB<~<2hq7oT7KK}G+ON+C!f`YH_ojWgI_V;^w z%E(w-S6A=v!#l{ynVT0C?d*(>hJ{_YAbE$C6%tAE4r^=c>uk1;&ef}Q`o_k=fl#Qe zEh7^iK03OyQ&?zfDkJ0Pcl-ACc6qs_<@xisZuRy)eG2bz@uID5L&K9Nqod*BN=i3w zR8~HGI5ia=EiZ3iAQJ8GFE8Wqii)bLRO;g5{5*kh<%+y~O3LCQtR|Y87cas)JbqkV zZD*&ZhC)qD93D0{IyhXu?B{p)?u!?FeG-&vYxnmD1_A=)cXsCUf$Zu$}%vxawR%?aPa>9+*}KbD^~&oJ38*&D=Mi9V zv!!KYqpi))PhH*BwWel!duS*=UQf@_v9NG;b#xSi(bcuE;PDm~#>a_7Jv{>hGI@A- zWQ0P|*VoogNf{iRno3VMGt<;eNEjSkT`etjbu~1Mj~^V|-mb6r^wiM_3F++I+G=a_ z_rH48)wQ&Ae!it8I9N-|$w@4pnQ3ba57*MNwav}FcW-0_hchy=wB&GRW~QfEEGw&P z*BFfH>BU7J&(u^^H7csFZ*eg<*Vw8|vawN93l8q=ynnx@#@$_0)6cJ| zX?3-uBO=1Y#Lus`_Wu2zp6F;@U0YkBaDIMtluR}`Y6QaV+XDj>iiwGyUUKrlz~p2` zhJ}TuW=u?9-@SXq#U36O7VvH>Dg~7o$Zn(I_#89bBW_r3vR9044NTY>^2L}@fVsS-9Ru)_( zDT&1r2r@G0bRv;Nq8>F01wO%Mvsg459uM|HS{nbTrKYB&pwW0dmz$fLl|?4QUe99X z<_ZL4a$H{GPG%;Vj7F!Vq@`tLak(T?N(!0G;bdllC6Sm&AaJ?a**qSclbA@Q z^7#S*iv|B75_vq4C?f-pkB`SGqbL) zxEPQ3_YVnSFiJ~{i|O>JsF)ZQtDu0(4GFQg4-2cRT3HzxNl8&tyK*Hes;6gSg2OR1 zynHz%q`!ZAyQap;NkPHgy}5a3r@K2OL`TQlyQyhs=l1R3U}a@HyPBG(PZt*x5@cj9 zUnY?j7gtu8Obw08mt$fE2j}OxT$sC2QGumBc}rY4WKySurWntJuBmKK}6 zxA*WNkEg17{(N-w{QTizS(%a1sZ%#^wzh6;)YLdS%FExn1y^loxOr1n7WV09&n73M zqt(=GY)VUacPAzi6ScK%ZHtSyw?{{#qAp$1(c$wqHfCoN6XoP2d-lD1c)YCag$v2a zYim!Qh(y}jva)FO*4EEIH#eJ_e)EmH`^?PSw{vqI9-n-oswx)0dbPVtp(rXUC}1$7 zqcbyXwwc+xIcjC4tDBH8Gz7Mglhc_qs;W5Lz`)d0Sy_C%zdstCnORW4#y^wEKcCOX<6~o!liBR-?2HT|F)Ru;?-i8T&mJw2VlAd^WXGMRGJ zzyn}1X*2=>ha-{TsngS`R6L$YOiP1VLZ=f5@OBdGJ~1&R1@0sgrKZNmqfj^;;E|Pu z$45m)M^mY}xy8jXxUf$i6*n)w96BFNm-_v7b^YO=8T17>NhhXPPOT)NdT3TPv%CfRDHl|RfrtaMn z2yARjOo+sZiItU{94jjg4HT-sA9fc92W4eX&zhR4sp4X1XEiln-?p}$oz6~we-#xc zr<$4v55QX2(z<>SJ%NoB$}S?>WYrm)U>eR z^XKMZ=4)tNy^6z)kKel|5;;1WnPIVa@6ONX<~li9moG1ObOZ;hs90I$<}NRfj^c2}#x^#YnX|LQ!vungrNbp`qjBj~H%3O!)apNnN|v*!aT_%gbS5Uw;iA)QcC}+et~^d~@auiS*#Xp{d#Nb)2H3t4h~;`ZD9eH z`_Pc9>*t^A>DAZ&{PV^}WaL+0$;0TOU{@0e^72YbZ1%&W#$?LL zojH?`u(Y(dm!2*!|Mk})AyZR(djf&F`nhwlv0Gce{L<89VsiR)P|*GRZ{Oa%Yi<4M zr`FbY@BaPoi;LdgpMS2URaW-$<@|g|$T#08E9c}K9{0 zr>4#vH5TjIHB(b6bz)*+K_IZTg^@Tn_vlegjg{4BpD8Od7;|&W%SA=O!LF`}i3J4} z6)aXnL})0LT2fM0mYNzG8W2DvmX zcsveArEFGitmrEcdBuIAt^mH69E)LehoE$!%K!}Y^ zOax0IJDWm@i%UpgFhru997*JWMiU%08Z99qF)=kYJ6j;2QWFxAlG4&dqO2?eAvzk3 zPEQw$v$F|=s3C+}A&CS33 zvbGi!^zp}+F6HL_^wZ3wba?pkC6^1c27}q&e)cRgQ&CY$3XgyO{NMk* zqC!dO!w-FZH#gtDy?t9>|HKJBy|%WWei|QlbNl>r9i8Ihy}iCZSJ!X8ftbpl{ebE;nXIN}Xqc1p;K9lY4tM1W*a-Xk8yiHToSdvI4!5+lx(cxt$-6yy zl9QvMapp{D=*-N^mrYHu8i$7u4<8)V)Y#c6D@R67O}%{C+-z-q`m}>Xd;6nD9UYF2 zr%qW}H8ebZ3O@1Kv-kY%CD0ug}eqNQQO5$Zfo=OR8#Zu>Fj*^bZjgtN>S0oBs+U=Z)b%_GxjkSWKaWhet-H zrk0jURwX~bRY(>RyzxDCy>)Y5UE;ctmch1G7t!;0wtgo;;r>A3L6cx?Q3knVnmX`2%b#;Ax9&dYlZH-P>Q330Ab{3*A+S(T{f~T;wBoM$# z78rQf6`SX^Rb#*Ua&do(coIkIr zDHQJQEiA;vojZ5w5{ zFm!eG^;xX@_g7aTzM!U-oIEkHw8Y`)=*Y^3gn;KP5}BLJ%LfGX_HJ#}*MmPE9NgRc z;6ZnHV4$|Pk56ms{(gUdXsC(`cn$08gM%oPva*RuR@TbO_;_NXqN17_tm~_*Jf4w} zib{0!?b{0r`S~s`W@Z@7z`(+SSnT3rVgfeY;-Xk=YpbH-@88mL?_NcPlastWL|pIR zZ)@}Qy>bO2uDiR_)5*!2nr3DK!RF@L8hH68CS>yH==5|(hPAbkQBqP@*We(;8Vn2) z5;{7dZ*H!o6&owLgT1|$77BIuE=0Axyfiff0(yELJ?iW8^OKjqe!aAGdwX~|DoR1Y z&=6v}Q&UMvii+y$bo$Z~#0>THG&FFyxw);aygUmFHMO|7v9X<<(o%@NhK3FfLiE+f z=JaVx%ZiH4&E8&re>pibGq45+1|S+{YMPr1F}BoH0|P_Dv@}?aC3Z5I3^83k-^4^! zH74fn-TU_;7A-ID>Dkn@xL8&O(IXd^`ueS{-rmqqb#(`ava;>%$w@5M$jIJaEZ*3d znZe^VG_985CpI=X7>(xh zOG-o{5-BDI>;;hs{33`ZfE|&Q1;^v#>2#4OH#aQ}jYgp)o)lOi;NLJ9u=0^e$;o&; zcwt#tbb3+}#2K=(a&vh+@Xyd_Iz1~(D1>+fSSzWi85v;rfcFP^6iG&fLSZn#x`Ip% zSXUC;ib`d(!5X5|@puA(PKULNLctt08ja7FWJu!UVLbxN1)e-9iNO#E;BGivN(!A0 z@s6~#UG40A<;u;QWo6UT)zw~J8X7lm z78Zhq6%e4KbL&=F8I1Lm6l-fApVHEWg}y!v#?sQmqp)ypuCFg4L0=ybn4IkIPfF6! zF)?8>CnrZoNhDoe9UZU>*49L#8#nayl9GmoR#plM93A!b(dgmft*!EMTU&W~h`elU zG&Q-p%E(w+78R|p4-ABbUA}B&l%2h^v%E~9sH>}~QK{S8uU?gx>+1gg_ex3}&eBoK z&kqQ2bw#6dataG!FOzuv98Pj_Y%Gx|6z1i@=nf7}Ny*76E6dDGObiW8OqA@x1VT(q zax#~jmzS9d7Bdc)k&%-Fk%9PlJRYJ1ELLJ7jBPF#j^l6=Yg;IU-8?>?LdnS~C`eCF zNQjKY<8yLKOZj{>IwT}1DLcEeQY1=D^zjJ|WwWcRtEyP6$jGoT3Z<~HtPK1RKR+~D zC@e1rTL6UuyP%+e&reJY35khevx|%K^J%o`=$IG=BQGyEmrjRB2bG$aS6G;y9v24| zH=kcrBoq>fFhe1p0Q)XP>0q`Bg%k?d2$H-YogNp5LgDe5nfdt;cZrQnN@6l0dH_*H zB9Y1DaKOtTlflQ}a;a4CLnP5Y5((xeV8mvJgxJ~n`htzKvQl2|;{!2;qN3T^J9ns5 zM@Kg|9&hZZ@pz7o=H~J7&CMet1qHso;HMWC_x9%H`TClf1qPOvPfRp4L`E7L-@GXl zj*kxvP^k_MuC5%;ojZMfbh?9sqa%|!FmUHiTAGcGnOS0DOUv!sEY|hwy1Ee&jg2EC z+1d8?8XCU7wY7_j67S#Nzq)#P8AhU+nVVZt(aOrigd~PcBHg(I&tz|JViFx)Q`6JK z=X-kE+M>}lHCxugpu)o8;pXPVL~Cm& zCn~j}p{9mNba(ghVKOECTemztQ&S;UoRJX}7O%23;gMu6! z!ozcN8X9tP!oqBAeSNvy`uf7cxHv~gZ*LB#y}hL+HPzG8!-G!m>>L^@ER2Y7afy$w zsu~=usYyt1ataPEC>R}WZ;y^vP&j|y+q=E}!Gp<363N`$%q%6Py?tm%EDjE~wG9py z3Y(kD%Sj}#0=e9h67bGKLgM2yGpni!3J8Rtpz!e2)RGd2K0|ydHMOuXFONtJ4h{=r zGAoW6_{Bj%WO7MKMFo=?6y)nmB$k&qG;q0rfiTa?$~rs6;;1NB*XZb?qTb$$inurj z2R}c7ps%l`g-CREc6FuEnwwi&nM@B4XXoVP%F31&K*ZA%hpVn`ZwEvi9U~(0^4i); zOH)$({Gy`x{My=*5;8eFJUTipO)M4)AsUvD0ArcQgZwoX%j4zc!CVUu$6&IuzzW1* zA|jw)S_3x z>}lZl!d!>7K_ns)!^2}^Gcu~G#9|c6-2BFkw6ukVgM->yTidU{*3{(l*VYymNF<0S z$Hff}E-di*#>NnTZFK|!Za>*85nqZ6%-5&H8v(CIXJ+4Xl|~m z!r|Q9+}+9K+S=CE%uIiOV`DS3;9!BEv-8fKni>kl+uPY0h04wC>S}C+{n*(VgQ=?O z>dMUx3v+Y~4;PEOyDKUZ5}cg_0|kQa?z+0E zva*JT^mIQzXXk{3vagvQq7nh(Qp|HEVp@Bf~@PPj9+ih)R zvZJGe1CiL+*w&VocI%d-BNp4x0Bg2~hn-zg66`O9g?@e(7GYsEHM6s=t#NS{7H)0= z!SHZT4}8Z?PBa=k2aV?BWMhLymzGvnQ>kul&dxYoU0r*7MuwlCn;VhX+B!5;Qi4Lc zyC)@8R`&K56~)B3yT`|ulyr9&7stf7xP*m)Zwk z*qD;y?Cj^4mDSq|->RD%^!xieJ1G=e1gUKWWgEtQmlf*@;JR%UA}C+FtY+`P5b*l1_>%{L|{RaMWP z&CZ5}ojIeSk)QwO&5IWtj*5z+B7^bh(RbhF=WA&JD`II0_&j=g-+bfgIXwL0MN^Z7 z#n)duI`;Sf=}!!fn<>j|;$HpuzfA>3MkP zZe;Y~hjw-o6K~%xFMD}?`l*&yZS7zGdT@ZjoI7{r3dE@o4_PdE`7>uwsHv%)oxD7A zb7f`l_*PcHo4s-+B4T9Z@#B^jH#a3EclV~I&CT|9e}4@P50B>N{r$c^FR#<5wX{T{ zM~~Lm@p#}m#mB>XUQ}de1}vUCcOE=wY`l3Ba_ya+2M4XK*4FR6clmN|?w^lZVWGD6 z*|QK!*xzq&4+zlGa&;ApM@Q@HQ78)wPtWY^{{FhUxVY=r-QBaY1_!&lsZzN>3`x~Ry@%hWV9w5n=&xU>|!0&nla!twFe*2Kgc zH{9I?f}x@AZW77X*1>^J@9F99=W=h|va^F3+})j(<>O;#7aLn%-rb#-2bNt}7|dqK z(ZQ;jnc3D>T}>c(c?AZt+4c2RRWw>qP*@n7T~kw9ip9FQd3aDL_4Uoo9FCu#iwn%0 z?(V|Em>4&=h=}a$mX^}eq$C#?Z|}4;um(6BfB%~|V`IhQ=H`Ne#6(Ze;9#&s3JS>N zh={njtgPl{SV^3nEiA&rD=Q&qlbPw`GZ%rUtbI+ zGgBbIV*QUAHc7>u7^Y;10BTU$j14(IJ1 z5&~9OSs4}!_8gsFSy@t&lH%s(4GD!_UJedeY-i`hL~*gd zKdd+v6~n{j<-x(GrXC(SIsN@LHPO-6uX}sv<_-=vHO0qUSh%62_V(~_P0brOva%*7z_--W0w&1Z+~_DC z4>pA)4}fy+vH?ko|BV~ zPGn?n@8)J>qqn!VHdqDQ+wJYXzN)IQ5L}&6^*8%*c?E`t;M- z*hi24_P5GPC8hV?b9J4a{rTs%Hhul~-@kEVc=&IB8yGOUuE*w{O?h0s_AN8dlA}|9x*SGV-&}Q{wsidUc-Pf;0A_axdKbPd{+S<&`Po9Ka-Jk#5({ug$mtWf14GjG9%fy7U z^9LWOsumT!e7U$77WVD8%F5ZfvEwA=sO7 zaSIDipH@_unMp}`cmV&fqQb)B^l3Z0`g+Lx+S{K!Yi(Uu_xSPHSY)KKvW*Qex8~@%8oGTv%nny4~EY zsd01D)(#Js#1GxvR8_saT3WWZJ34?vX>BbQFD`a1&d)C_u-T@j1_mV3^z{AvLgBS*s;c4Pot?9@A`!$u{rnmmr>6@G9UWCw zy}cV7S6AEH!@`V>0|FWw*VcM^qM{59A!*u+Z1XU?2ujROI4fWE30Q)3dl(QsUyGtsN55-hTf+u%VQd?d|jPCng#j zLqqlSZr&7&r>0t3!osw)?ClE+H#VlH$z)?=D=QvvadB>r$uu}(tgJ))b{&`>kW{sw1L&IPHy1gA1_HX}o@+6!6=FRutGZ>$Ia^i%a-`?Kaw>>>N zIwwwCyEZoV_U-Dbo7;&Kii&M*Z;x7f`g`x4IN|B}=n*hRtgTO+xO`bGe*JoD3x~US z)PUKzylah*x8XCZ_ zL!p4B0URbAE;$)ueVLiyw?V9j$IHoq%x6*(fdDZ)E|)?{Nx|bKF%95g5C}9H`>4_B z?_!LQ&7e?@Gqu3xU@+2-8l4XD5E_li1P(WyPNhnC;*$I>aLA9juimt5($SN&H^dw9D4cwBO&-@ecB8^E0;D$7e}1}mX#W=Z|F8ORx8J^vqzbfXG0JEBYFG( zejh&Z+fV;L=^sDcfB$a(_0vh-;kQpAdA@hw0o?8X&7J@2JHU7O?he20|927o*QiRa z3qRpHl5^hmkG}`W{Uiv$S_o2usd9eh2vL`1o({EICW^Q*s>oaIb&< z>$m6qPxb%*xW|9}tP`cc`DA~K-?T)}1 z1MQeZFZND5CdqV4_U_~UfBfw6P7Ia`@J`{Xa3#rKz!n4kEQv&?v)PiY4h{zy9v%;P z<;lrONx&8r3Ntc*(+#{2h!P%iN|{X1j-{q1CB?=<_7pf{BogppCFil(kXK7dNl%A} zIh6`rG+@VOW>5y|fJ`4CMiHS^RP7dU>;^N}tf$Nf=&tODFg@)qs`T12<0zq7yj}Hn3 zak%7X`Bo2#ng@oQ_(pO=&v8(+K_9zHeo>{)HCrKOaVyL)f% z!9ioAjm?=ec6Kc-Po98sM@q`nw6qkoGzkgH%D^Pp-Mx2@M1m|TkGH-KOmi(QWo6K9 z%+3N=0qpdyuK9V9$im|CWnbT}uD!jku7CjWDeCKYcZY`J;&gNz9YM)9F_Dy{p}wnicWD~!wC+1c7+v9z?VUS%-W z*Z1}$`kC->&}s+-`uZ0x`1y5rLnOl7Tt>#rtGD;jqn;j!EV#S3wmy6~I2axdIg{F2 ziEd6w$-sceyMKRf4t!NjO$udZW_FfJmFVU`DJ2j9n?EXQZ0x~;_B`d3=BM|KGgHlRO4S4sPM-ARV zU7bo@Tif5y%F@!3IZ_#ojBIY^<{BEFKMzW&^>v6Ko;wG+xx>T0J|7=NMOW9>)+bLu ze+Akhv-)~l zTUl8TkDi_`Z|-Tsi~ns zrLL}mdQI{U!^0aJ@D3L)`12!OabnQhj~i-dC=;y0*1Fdj?kpZ@8}R>C>5+ z=;%wA^z?+nogL8oDJg*_ZfOaao{EZBt`G=IOIur@Si5v7Hg;y_;luKB@Zv$4_xN#b zt&NSWthe{=+fSZ!blkjo{=BtyRn_)(Pmhn!#fy@67#+4%zFDx`Mf$yQW_wcZ%$II)= zm0PzuJD)!X{gsM}qhn*^vuD%OQBiVopjg}4nV(NiR#XH{97H8R`K6>pB7qhwBSTXY zqExf94L%I_Ubw#x^#xv-R|(rTzVH-(FuA zi;a!Xo%8VM>jOrahsTvG?(W^)&z}zuLo~(7sj2DNv#F`bNI5wJgPfe5ow>QBBw&LA z-)nvzk5^JsQK3@r-{0Kia3E5YkT5rQa8OcWWu>eP@9^+pWu=wX#fzZ2J3MS{wX*}( zCyb@;ZcyM^SyfdX91IT!2g}G985I{ld$zWQ$15xA=wxQ@?}HLjU0p+i$=u%F-{*2w zRi&j-sOjn5U5N3WI|uByt*wFr0|Q|04h%eh-reoy2JDW$zE`h?hXVo>6r7#g+h4t! zm9JHWR+ zd)CSd=FH?|SQxPX1cHMDh`xZ16h`sho=|9L2(h)9nFkL_OU=!tq;B49Xt;mBw${bv z@?}TIii+iBP~#~p0|#$&b9_7{#lXPECOdm+X>yW6F*CblTaE*4EfarNT~|kKpjVA zBx(a1jd;``GYc$TNw!v^V@gRup^}n#ywXz8F*rKjyh$cQeh6|0T3S|COy=FY0|PYL zwQD*$C{$b9%uGp%hljSdmsdr_;$lxvLV}5jlT&u~?CjWBTAJNaOHCab8XxEL9UUPj z0i2o4Oixc6o4B~jO33QExmjBSD`#}HpuoomxBv|ev$Mbvu&@XSsHm8m>*$DyF*0&+ z5DKTJ?%cuSO--*|qf!S3`uj;FV521__VrCp3Iuj``ufq)ot=w|)zyK4R#x%xJw2nZUtYSz;XHo4yGtOP zK57ic-~RUYZBx@%U!6Gd{`;k+5D9H*(boR_b9?)S28dike#qZnBm@!uJ-x6HxSF6JqtkPb8Ys(RV<{A&P$XipFqrswh>r?|>FJ>+S7mw1Y!nAY|ftdx3Z0?G3!Fwl;wvGSbfvhbu2{ zYZHrs!2o(uktjVKG^kNgEEX_~qN9C%V`GKFy1JqwJU%1@jRvMtWhG<{fwgq^ZdVuN z_3i8^l-sw*#S8kQ`6vJ(8sm6hlc9vy0{b-&Chprp-?(HH*WCxqoW-iad9wT1%jEGfdLFg zPtV*uJsp%GOs0i}l@&bE>@1sYVPR$lnGVp$>FO#dL`B`XbMGFg;32yKI_`o3u;<*} zZ{L3LqQBqU8+hX_Eug%R*rfIK&z?a&3Rzh_J(1|agM0Td7(#V%a%^z83{_N=4B!Qu32D=Sd%_w=~C zLk)_W8c-+&1e`sqr&nD3>ec2Z7JKQEnp#H2g9kf142JSiqtkbGKue^mDkT*W0pD&; z4%kBe{&((dZRO|d>76_YDyCPjy1Sj7p(@0{z+e6{GZPSS?wqM45( z3pAP}TdJvf{(Nxo-MgEcB_%g*K$VA^EL_yFgzxj8fx6z+9(ySrm!v9YqU z($e4)uC8`=rlh#K1_q|3f%i)!#>Qf?e12XYhl9aD4H)S2cmx6{Vj+)`o=zr9G^=bj z=s8FvnA4E^pin@)BGH?n(NJp!7_fLeD96*&LC=z$3@X{|Y|!4K(V?Mnaj*{MQq)YPP>2L*Y00<*KVH7(87 z7P5w2U7#z1xo&2bk+HhEu^|+in}cR(aBz3Gz8>s3d;8K-P^X55s;OC8!rbWXMWGZG zwX_%v&`}5kR#y7@lGOotcZP=1(S3cJn@vqVK3A{0xs{bIEVQQyr{4rgv|YKl%bG}O^aNf{Zrf4{WU!vk0at*uj2<>j!ahKE;F3=9Cb&Cn27>wSI2 z#esn)CLSKSxp(e>rDtjB>6x25HrCTaBsw^FdJ2W3qy7Cfn!UZFBa1aOG%~{BI5=Fp zmXOfcc>6Y=@8V)^9u);lDS^Pl!`wVPysBzsq`Epb*4Y^tY$GFeb!ha>n~{-aWuv3O zpS^JdIB%`3wY4~$iwkgOYirBOl9S!s{QbFHVEkdRw{8Ul@cEscz`gMF3=PfAy?eK< z4TrO}y?K+(?(6I5z+%nJOifc#Iy!oL84Osvu-NYIyLZ#lEG)pL7#afRrMtVnepD3H z|7dOo^t`;9n!ra33Q|_SeqAJ*pYQ2GpW1>PzatmXmYl<^Yekj5E?o(w6i1eb-cVl$=2WR^&w_VPM;QdhUA=*g4W+0f9hzLJw; zvmYHbsDY!PfXA<|KY1b+8ya4^6cYoLoSK_$Z7*B^4b;Jb#M^^fMCa?IE%9!TQHy z1%i?ip%9cSC=@8;^7DzrkdW|jCbPV}tc*?%4h{^YQY$MP8hE_0Fi%f38tQN4=ElW& zc|jbou1+ilmJbS*l~rF~Qi8+z`i6wCSk=|#O91L84 zVDTp>2M0^I%OVk#8hg}0tuGKTnV>hJQq$9+<`;Mcl8iFsok8UyQ4)hS1vzQ(Z?dvL zvkSHyjh3HZRFswmQBMk`va-IO!EkUeFbE5SXx8j38hz;!c&UqvdwaRL+S=cIbMq#| zh&nrAoERIIl^q<6jRglwNoi~6{7Zknr)xXV3Eazx?vU4^--BpMCH_eEh?QfBF-bd+O95{tz0vv-9h(Wn~Hq z@4qk6zxMSR8-Mz#yZbS7@V)mWJc9N0Tem>jQC0QpuNxbIfggMzBa@T!{r9`Ov9X_i zdhQ&T`{vDe-?3OSGE!0$%HiR6-w6bYiqg^q!jmUI{gj(4EBpTY-rkFgKmJ%#qpA7P zN6yZpqu+nu+-zw0$3NQJkB|TF|2;8bXZQa5y1E@5Z{P0iMMQk@g_2Tn@vpx=dqyCf zIU_C2U_5>d3e(G%&z*~lo1fp@WV5eaIeQj`T3C4eSS;4oK65502vCC9iL9)j-{>gB z8gAXXc=6`V?(SEw1_wPnKqXvV{o=*kTxjUG-zqACy#TC3IXQ?LZ*4t#BoY}N>E@D> zW@dJF#9~X!t5=~`(t`&@MaITbQl6eYJ-fR#H8*ZZNjW;Uv^;pw(c$cT`t-GHwY9Ha zEiJ{x$;tu)eQ$4Ti^DNKYAn{hdy9)qrk=YFtI=sK%*5=|OC1q}2 zQE_-UKOYwM!3QT!czD2C3#=A>eLFigyQ{0IiAweJ3k~IPYHEs$350-v@NhP}va-CK z$&88$4~MvdNCY_v3J_Xf5($IB;h-`J@K6#H!BWb~0*?ZNiH-(sQ9(gY4w)Pk6&VQ{1hJUQ zO-_dYKyHP{BN8DWBeBY`SQHA4X0wkKzA-T*QdU+@4vPiV{-{(~O`zHzc!f|sKvJzo zvW9|R2MS&Av6xIeJ|*Ry4lW@9sseyDMI^%0LN18I0p$-^QxI#AL>eTq3s{{b_7jBy zc_L67Nx1l(opWe||9bXV8#a7KourHYELZ$rbRtF5d~pSH27tJ~RWZ+CGybH>!Py!^=%$gilY zo15q6?(E#VN2MAVSXcm)X=#a0m*~l+re&B=iX7zP7#x2#MuT1Q5nKW}NdapTk}JG-{F!$YW%@x>QLMs;=Hf4{U85pn92 zl2T^olPCN8EEd!+NKFOhMS8lSA=F7(TH4rvx_?mZr@tTSPMDhmN4vRsd^|G~l;UAw z5R0y@_3+Tq2@UP(+1rD9Lb|$MUJVU9JA;F~pajnyqG7P_Ks_n2oKsW5>zAk;VMIX92vBWRy+{Q0pMcfd*qE1xLbC|SgK>8D^@WIK zWhGdgfq_|Bot-r`7>tvXmlv1Y-VPS0hljs^dOGA1F&GbzurNNqqJqu#@^W;fQfFok z4`D5mmbS5hnhk4fv9S=SJ^T4*u~=Rnsyy7g_v4S%)mN{6 z_@T4&$jIy0b#>a>@4s(rJ2CN(e@sj`I)47Swe|4u+qa90uC5<_WN3K%_S?4y2fn^1 zPMka`6#nHej~>Owe)!?ZlU(jkKfQj9$AA3sC!dhXKmGLY|DK(F?%YQo#l}8={`PHM zovP|5pM-?$?EK>&EiG5C{{Hv&_7fAo{LiF+xzp+ zqoelrAADe70DRc}{qXQFzED*yEBp1=M~~3xZ@!U}6AIyOG}@_CUwxIFyubhQC7&-V z`}yZk{qyQ{&{5N)-BMbx3v89)5;18B_*Y% z#^*y-LjpnJs9~{lbE~URk5g3@D&lNz78ipeJSYfuxsHy&Kut|Ix5~=3wSfT?N>XxYR znx&;gVnBd|?GAc6A0IzIIvwh|vDyCqZf-Dko0~ho1)6=JI+iu&8N>&swfTEIf&N*i& zG8CyuRTM=*k#iA?oO2RHK$2t?Q9zJrLd7t4n&~*r)-}(cbZu*0QVuz4Ysxk3C+z-O-v@&Qee+yHaEAnCM4L{n48mR z?d^SinVH}WLgf#a1{hn$#)*j?9ksP&vbwr~L45qc!02dJmW73;COYJ|Z&y}&dg|$+ zS{@xGd;xe=cXwA;Y^@T3S^V6=i6MGY#AZOs167-o2RQuB@!Bm6lpr;WHH$ z!UGW+3U5B9DJd!FV}R;Q^hil0DivOSIvsPB$jH*ty1JAUS6BF$3JO|Ui1RFoL^wn# z6e}wShq$=v>Z&RR!^H)T&*I|J(zrO(Lt$Y#ImN}e@_TqhL}X_d6(uLbDUWMIZf-&X z)EzV$2t-(qKpT>tjt(xt7Gkrp&%@J^m{?kxpC1w7=;-Pi7gxpI;3$PZ1HF{QL|0d9 z>(J1WlE%i23^z9u6FB~ z#j2<%DuS;S)WGU$4ks+k($c{pKE9y=)sl^kl~r_fRTXjET38UCTpaZ#CVqaXAK7d$ z>Bwa8Q*ae9Go#acd#9&MORcTpd@m}Rp6=}p57*EzGD=IkeS3H~Awf@1ON&P9?;js$ zvn?!CRegQoN-iw4v5}XzvB}Nt=zuF-QPIKzE}G(EPfr~kXJ>ecTUyZnfS-4Cw5Nwg zGcvNUNKS@REHY9{OIH_tS@@v|mk*AP>}*}#OP5?-;mB-gKn-qTQCfgqN&K79(k47#S};70W>woj`8tDM=vk0uF~lzPk!@_ukWKr z@86e|fg9xGv#{{P4~>nAipP#QI^MbS_rDX43@xp$uGg=-yY=)A8~~Z<<;xp4aLtyL zt*`&^;qF}*mm^0mU*>SOx98`{WDyZr*^(06_V+(_?93TZidI&_!j2r_<&BKQF-)T! zKYr*CuBdO{W@n#1{q@%*($dmTKQ%SU$sIgsYdbad!w+3unwm$Cnwt*~fB*f>n>srC z_9-a9wQ=KykrCV`xC-|5ffaS}VsY`CH;aowK|(^8FBcTNef#7Ioh~XWE}odUwY9yS znTcLBSdp8X`T19`f`L3V1e&y>;*lf9T&DBZR!@(a885H0a%JV~*PuwD-o<{^+gnFq5m^Yb$@a&}HjYiz`GIy!oJrKD6;h5$fSk13kH&Nv$~M$b1pNcd2QbpXEeQ)lC6}BW9gS6SLPBQdF3*HusDcpzA}Ssq z7DjZAi;4;hcV|@Sa-qIMg@$PxUV+fTWo9NMpeh6REin;nB~*~0RM^^DS;2uiGt<>Y zp{S{;t4BwJ|H0unI%;ZqdEr`HQQ_{ctcXk5GI=~-Rf(ZOKgwvSI+ z+xU2XK6t>Mo}HcZ^KiN-DY>{bG(aB!zm2VJb@l8lCO(pqaK>Qb18tL%QhGYhj^5s4 zV%M%ECC$xEPe(^zzAPh?l(e`=Fq$u20!{GMtDGDOiGv5xJK5SQFTZ+q|9(@`!NIq0 z;fLC@M@{X?8hHBHvIkf?mcuUHum-FH*c8C z!-qfn%*}0X?!}9=G=BciK67vY1+lSFS{hv?__any_4W7f*Ve`w``$e(t1rKlmaeP& z``;fu0{QUt>5L2z#eIDV7UA>f8yk_4hYo%9Rbb%i>bKv5LN6%j>wEX^))wkUbWmq! z-@mV`Q&c>3$kepA_xbbsdiZE{b-TOYyy@=N;?go@WC$`GiFEkzg$r=JU^aZ}l%!;K z_N!Ox>rqiBP8>fT83|fXaSFo!a{H`AK&TIU|%mR#Kee-3JL}VfpedhCMkLH zq`Ujr*t2J~wR(DI&Nw>u_HJx|TLzC-d;7*lZ?B`{sZ&ZynVIN)`}kmPmyt0s(bMDa zf9aC4GT~_gbwEKOGIC&GY%DF!z(7MII(lGWVj?R`Utdnn$ET@ja4;*&$Vf)U&8@1c zw>LdqPw&zt%s?k5%FAtRKq9KIpP#>R!_N;6$+EKfc`!I-WHdC=)9>8_AZ#8qQ3kqlJYI4!XKza%1DITLlFU4w{;to@He{ zJ$ZSKj_T^p&TMvLV`?fUC(h0s&dr+~j*E-BI^ipCYYPg})pc(ak+BE(Xq30eH}VWQBi&U`uZnN zpzk|<8gqcf#eo4A7q~g0z6101`0-Px;^Wu38Ao zIGYW(WO(?oWBmMaaa&t2Uox4;j_uv+>524-oE#AmIKyw>e)q1SK|x{vej}rSf$zS< z#6wWf&=6Ja=%}-^hzNQ|pz4!INIl5TURW3%_47M(=GwK?R1jdu_*~UB4>jt|n^50rX>mA9 zOJif;zv}8@!ap(+5g{w9q{Lth4-XB+#;U3+C@3z)($bg=zIXuzDj%PyC^RpqUHts)?1F-@o{x?7;nKAd6H7|iYzoD}!PS+) zzzl@JAd%eNX|#fZ;$kM#$A?6Uj0Ayb_u7||f_VcdR#0mbokI|d(6PZ3lu&*^?GY74 zp&*BZ#lqB=;B7%i4)+_OumV|>LIJawxL!p>5d2awP|*b5r=o(%bal104Gk?UtgX$+@bk&ce;?krYKO+^KjYHLf>?9I(7 zDbCKOroO&xHkjeijeC28h=a9=iwmBqxR}kRQoX(X{lPs2IUOqiu)k34U|o@!SyYsk z78;7W9_tFQGzfZBZZ5iBo}R(MM7@vM4QhYTnZm-L&jE=iH#Z`})fJq_!a`K+yEAeS z61}{@dCKQ*pgDr`gh@53dS74MUr@kcUDyv z3W)@tePLl;U1p}2mzC9SHHjL{-900tsi~wSBm~rs#6veJB!^|uu zrn$MZGbP2&&d7*NE-!Cu%g%OpH!<<`MO~Vo@8^deN>)}~U4A~9Y-{V~1-c1X@bH9Y zWK>t@<&nvRaj%l z=x0VpV+NU;>fvE!6%de{TV0)%g**UnZ_FTzio(M1p6K-Q^6F}=U7eiJsp4>UJ3%1y z;%M;qkBCT3#hwJ!7kHjrL|-tSd3K@bN*qPiAIzc0vM$g2@5V z*T>W%Bn16kLKpE--w4kudi-(!bg)A82#PUOmFU-EkBE&83JM5-Cj^=fa6YM2tX)A2 zB^(*KxnRX$b&OSTVq!#uzdtyDIXNJSdVAxY!_NWAn4ce=&Sn=B#K-&kdU#MMSy@nk z1_imfffkJ8H8Rr63tXrCeBAc%0D~|;zp#+WB>o~j9n3;PtpdGUYAT^`WU=z{GBcy2 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 XM1Tko0U|&IhyW2F0z}|Hk-&ce4Utjh diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ebfeec172..3d88c7cc1 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,12.8,, +Version,+,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2076,10 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,?,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,-,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" -Function,?,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,-,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,?,ulTaskGetIdleRunTimeCounter,uint32_t, -Function,?,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,?,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,+,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 25ce710b6e734f6325a35dc3c630ea0f23408c55 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Wed, 8 Feb 2023 04:32:23 +0100 Subject: [PATCH 28/75] Added multiple apps Added Geiger Counter, Nightstand, Scrambler, Pomodoro --- .../plugins/brainfuck/application.fam | 15 + applications/plugins/brainfuck/bfico.png | Bin 0 -> 1822 bytes applications/plugins/brainfuck/brainfuck.c | 149 +++++++ applications/plugins/brainfuck/brainfuck.h | 3 + applications/plugins/brainfuck/brainfuck_i.h | 89 ++++ .../brainfuck/icons/ButtonRightSmall_3x5.png | Bin 0 -> 1738 bytes .../icons/KeyBackspaceSelected_24x11.png | Bin 0 -> 1977 bytes .../brainfuck/icons/KeyBackspace_24x11.png | Bin 0 -> 1979 bytes .../icons/KeyInputSelected_30x11.png | Bin 0 -> 1992 bytes .../brainfuck/icons/KeyInput_30x11.png | Bin 0 -> 1994 bytes .../brainfuck/icons/KeyRunSelected_24x11.png | Bin 0 -> 1984 bytes .../plugins/brainfuck/icons/KeyRun_24x11.png | Bin 0 -> 1984 bytes .../brainfuck/icons/KeySaveSelected_24x11.png | Bin 0 -> 1853 bytes .../plugins/brainfuck/icons/KeySave_24x11.png | Bin 0 -> 1863 bytes .../plugins/brainfuck/icons/bfico.png | Bin 0 -> 1822 bytes .../brainfuck/scenes/brainfuck_scene.c | 30 ++ .../brainfuck/scenes/brainfuck_scene.h | 29 ++ .../brainfuck/scenes/brainfuck_scene_config.h | 6 + .../brainfuck/scenes/brainfuck_scene_dev.c | 16 + .../brainfuck/scenes/brainfuck_scene_exec.c | 16 + .../scenes/brainfuck_scene_file_create.c | 50 +++ .../scenes/brainfuck_scene_file_select.c | 34 ++ .../scenes/brainfuck_scene_set_input.c | 35 ++ .../brainfuck/scenes/brainfuck_scene_start.c | 55 +++ .../plugins/brainfuck/views/bf_dev_env.c | 419 ++++++++++++++++++ .../plugins/brainfuck/views/bf_dev_env.h | 15 + applications/plugins/brainfuck/worker.c | 276 ++++++++++++ applications/plugins/brainfuck/worker.h | 9 + .../plugins/geigercounter/application.fam | 13 + .../plugins/geigercounter/flipper_geiger.c | 227 ++++++++++ applications/plugins/geigercounter/geiger.png | Bin 0 -> 8048 bytes applications/plugins/nightstand/ClockIcon.png | Bin 0 -> 7920 bytes .../plugins/nightstand/application.fam | 13 + applications/plugins/nightstand/clock_app.c | 338 ++++++++++++++ applications/plugins/nightstand/clock_app.h | 39 ++ applications/plugins/pomodoro/application.fam | 12 + .../plugins/pomodoro/flipp_pomodoro_10.png | Bin 0 -> 157 bytes .../plugins/pomodoro/flipp_pomodoro_app.c | 101 +++++ .../plugins/pomodoro/flipp_pomodoro_app.h | 32 ++ .../plugins/pomodoro/flipp_pomodoro_app_i.h | 31 ++ applications/plugins/pomodoro/helpers/debug.h | 5 + .../plugins/pomodoro/helpers/notifications.c | 49 ++ .../plugins/pomodoro/helpers/notifications.h | 14 + applications/plugins/pomodoro/helpers/time.c | 20 + applications/plugins/pomodoro/helpers/time.h | 24 + .../flipp_pomodoro_focus_64/frame_00.png | Bin 0 -> 1242 bytes .../flipp_pomodoro_focus_64/frame_01.png | Bin 0 -> 1215 bytes .../images/flipp_pomodoro_focus_64/frame_rate | 1 + .../flipp_pomodoro_rest_64/frame_00.png | Bin 0 -> 1083 bytes .../flipp_pomodoro_rest_64/frame_01.png | Bin 0 -> 1080 bytes .../images/flipp_pomodoro_rest_64/frame_rate | 1 + .../plugins/pomodoro/modules/flipp_pomodoro.c | 94 ++++ .../plugins/pomodoro/modules/flipp_pomodoro.h | 54 +++ applications/plugins/pomodoro/scenes/.keep | 0 .../config/flipp_pomodoro_scene_config.h | 1 + .../pomodoro/scenes/flipp_pomodoro_scene.c | 30 ++ .../pomodoro/scenes/flipp_pomodoro_scene.h | 27 ++ .../scenes/flipp_pomodoro_scene_timer.c | 71 +++ applications/plugins/pomodoro/views/.keep | 0 .../views/flipp_pomodoro_timer_view.c | 195 ++++++++ .../views/flipp_pomodoro_timer_view.h | 21 + applications/plugins/scrambler/LICENSE | 21 + applications/plugins/scrambler/README.md | 16 + .../plugins/scrambler/application.fam | 20 + applications/plugins/scrambler/assets/1.png | Bin 0 -> 1964 bytes applications/plugins/scrambler/cube.png | Bin 0 -> 96 bytes .../plugins/scrambler/rubiks_cube_scrambler.c | 115 +++++ applications/plugins/scrambler/scrambler.c | 102 +++++ applications/plugins/scrambler/scrambler.h | 3 + 69 files changed, 2936 insertions(+) create mode 100644 applications/plugins/brainfuck/application.fam create mode 100644 applications/plugins/brainfuck/bfico.png create mode 100644 applications/plugins/brainfuck/brainfuck.c create mode 100644 applications/plugins/brainfuck/brainfuck.h create mode 100644 applications/plugins/brainfuck/brainfuck_i.h create mode 100644 applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png create mode 100644 applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyBackspace_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyInput_30x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyRun_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeySave_24x11.png create mode 100644 applications/plugins/brainfuck/icons/bfico.png create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene.h create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_config.h create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_start.c create mode 100644 applications/plugins/brainfuck/views/bf_dev_env.c create mode 100644 applications/plugins/brainfuck/views/bf_dev_env.h create mode 100644 applications/plugins/brainfuck/worker.c create mode 100644 applications/plugins/brainfuck/worker.h create mode 100644 applications/plugins/geigercounter/application.fam create mode 100644 applications/plugins/geigercounter/flipper_geiger.c create mode 100644 applications/plugins/geigercounter/geiger.png create mode 100644 applications/plugins/nightstand/ClockIcon.png create mode 100644 applications/plugins/nightstand/application.fam create mode 100644 applications/plugins/nightstand/clock_app.c create mode 100644 applications/plugins/nightstand/clock_app.h create mode 100644 applications/plugins/pomodoro/application.fam create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_10.png create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app.c create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app.h create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app_i.h create mode 100644 applications/plugins/pomodoro/helpers/debug.h create mode 100644 applications/plugins/pomodoro/helpers/notifications.c create mode 100644 applications/plugins/pomodoro/helpers/notifications.h create mode 100644 applications/plugins/pomodoro/helpers/time.c create mode 100644 applications/plugins/pomodoro/helpers/time.h create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate create mode 100644 applications/plugins/pomodoro/modules/flipp_pomodoro.c create mode 100644 applications/plugins/pomodoro/modules/flipp_pomodoro.h create mode 100644 applications/plugins/pomodoro/scenes/.keep create mode 100644 applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c create mode 100644 applications/plugins/pomodoro/views/.keep create mode 100644 applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c create mode 100644 applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h create mode 100644 applications/plugins/scrambler/LICENSE create mode 100644 applications/plugins/scrambler/README.md create mode 100644 applications/plugins/scrambler/application.fam create mode 100644 applications/plugins/scrambler/assets/1.png create mode 100644 applications/plugins/scrambler/cube.png create mode 100644 applications/plugins/scrambler/rubiks_cube_scrambler.c create mode 100644 applications/plugins/scrambler/scrambler.c create mode 100644 applications/plugins/scrambler/scrambler.h diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam new file mode 100644 index 000000000..6e2b6d1f9 --- /dev/null +++ b/applications/plugins/brainfuck/application.fam @@ -0,0 +1,15 @@ +App( + appid="Brainfuck", + name="Brainfuck", + apptype=FlipperAppType.EXTERNAL, + entry_point="brainfuck_app", + requires=[ + "storage", + "gui", + ], + stack_size=8 * 1024, + fap_icon="bfico.png", + fap_category="Misc", + fap_icon_assets="icons", + fap_icon_assets_symbol="brainfuck", +) diff --git a/applications/plugins/brainfuck/bfico.png b/applications/plugins/brainfuck/bfico.png new file mode 100644 index 0000000000000000000000000000000000000000..b25368fb53e03b73878a9aa17a044c2d8dffea4a GIT binary patch literal 1822 zcma)6eM}o=7(YOmKyZGbBNNT#*c_9!cReT{7dirMfhxy3z6O4=Yws<+(e}=}D=ip7*(DuemTUCTeXIf*>)bd_xg@E9&+&;9yN=+jdK75 zNgO}WHlAeI8at9@wST^cjE;Wo&AI%pY})Zgdviz1zO505+S5*dbUq^E9Q0s*?vo}AfY~MGw zu|Mzolpned^&i|}@9r|UW{rFobKix^-<~<~`{a9D*2O#=KX^8w_sE*&4b4qS;g_z_ z%>ItO>znt#TrbxizB$-4;%pp9ch2lSSGO*{^-4$cf!Wn9^V_xE*PDC9E7G@MaPIMA z4|>R~WYsWTmK4E!G2Km^j|*v-P2Ti%9v(J(z9sA0(ByRSCR5*&W!-zmFpiA<@d0Pp79Ia%{^SR_JA^Z1H*V=KHF3l8&g!tuPanB6%y+!KYUx`BAxQK?$`gWgbjBe_ zcqCU+E|=2M$QM1=orh$50Da& zB0#ljt(w4KIErS9tc@x%g0It(iCNdsPPiQFXx3II!iD;`{ux2YWdl}(wAWi=})ahK5Ey>0jm~fzeL6R4UrIK0! z;6*^<-J%s3Yk*5mz)+0+$5K3LV69PsLrH#lrZ> zXr6^!$r!q1hqEFuDs-4sl$V2=jQUhfMrx{xRKr7+N>L^!Qw0iut4g-}4_vZtbMH0)HCkr@LcL%}2g`%FWsNTP($QYj>* z(IycY6aiEBfq9m*)&3XM*WD=mWx^Pmrt&#S=Ed4YbD(F7!HdgG0i%Oc4uV09RY^^U z2_nzBt$_6jfOd(u$sR@o*;VlVbXc~{#=-P!QM1yws79?J)Ts+tj~zgU;G8rm6p_A= z6TYGVUoWj7z;FM50{qyO1OydzxqwVu`Er+6B@scene_manager, event); +} + +bool brainfuck_back_event_callback(void* context) { + furi_assert(context); + BFApp* brainfuck = context; + return scene_manager_handle_back_event(brainfuck->scene_manager); +} + +BFApp* brainfuck_alloc() { + BFApp* brainfuck = malloc(sizeof(BFApp)); + + brainfuck->dataSize = 0; + brainfuck->view_dispatcher = view_dispatcher_alloc(); + brainfuck->scene_manager = scene_manager_alloc(&brainfuck_scene_handlers, brainfuck); + view_dispatcher_enable_queue(brainfuck->view_dispatcher); + view_dispatcher_set_event_callback_context(brainfuck->view_dispatcher, brainfuck); + view_dispatcher_set_custom_event_callback( + brainfuck->view_dispatcher, brainfuck_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + brainfuck->view_dispatcher, brainfuck_back_event_callback); + + // Open GUI record + brainfuck->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui( + brainfuck->view_dispatcher, brainfuck->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + brainfuck->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + brainfuck->submenu = submenu_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewMenu, submenu_get_view(brainfuck->submenu)); + + // Popup + brainfuck->popup = popup_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewPopup, popup_get_view(brainfuck->popup)); + + // Text Input + brainfuck->text_input = text_input_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, + brainfuckViewTextInput, + text_input_get_view(brainfuck->text_input)); + + // Textbox + brainfuck->text_box = text_box_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewTextBox, text_box_get_view(brainfuck->text_box)); + brainfuck->text_box_store = furi_string_alloc(); + + // Dev environment + brainfuck->BF_dev_env = bf_dev_env_alloc(brainfuck); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewDev, bf_dev_env_get_view(brainfuck->BF_dev_env)); + + // File path + brainfuck->BF_file_path = furi_string_alloc(); + + return brainfuck; +} + +void brainfuck_free(BFApp* brainfuck) { + furi_assert(brainfuck); + + // Submenu + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewMenu); + submenu_free(brainfuck->submenu); + + // Popup + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewPopup); + popup_free(brainfuck->popup); + + // TextInput + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextInput); + text_input_free(brainfuck->text_input); + + // TextBox + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextBox); + text_box_free(brainfuck->text_box); + furi_string_free(brainfuck->text_box_store); + + //dev env + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewDev); + bf_dev_env_free(brainfuck->BF_dev_env); + + // View Dispatcher + view_dispatcher_free(brainfuck->view_dispatcher); + + // Scene Manager + scene_manager_free(brainfuck->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + brainfuck->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + brainfuck->notifications = NULL; + + free(brainfuck); +} + +void brainfuck_show_loading_popup(void* context, bool show) { + BFApp* brainfuck = context; + 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(brainfuck->view_dispatcher, brainfuckViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +int32_t brainfuck_app(void* p) { + UNUSED(p); + BFApp* brainfuck = brainfuck_alloc(); + if(!brainfuck) { + return 0; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, "/ext/brainfuck"); + + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart); + + view_dispatcher_run(brainfuck->view_dispatcher); + + brainfuck_free(brainfuck); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/brainfuck/brainfuck.h b/applications/plugins/brainfuck/brainfuck.h new file mode 100644 index 000000000..2e58321a6 --- /dev/null +++ b/applications/plugins/brainfuck/brainfuck.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct BFApp BFApp; \ No newline at end of file diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h new file mode 100644 index 000000000..d3d27dcbd --- /dev/null +++ b/applications/plugins/brainfuck/brainfuck_i.h @@ -0,0 +1,89 @@ +#pragma once + +typedef struct BFDevEnv BFDevEnv; +typedef struct BFExecEnv BFExecEnv; +typedef unsigned char byte; + +#include "brainfuck.h" +#include "worker.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "scenes/brainfuck_scene.h" + +#include "views/bf_dev_env.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define BF_INST_BUFFER_SIZE 2048 +#define BF_OUTPUT_SIZE 512 +#define BF_STACK_INITIAL_SIZE 128 +#define BF_INPUT_BUFFER_SIZE 64 +#define BF_STACK_STEP_SIZE 32 + +enum brainfuckCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + brainfuckCustomEventReserved = 100, + + brainfuckCustomEventViewExit, + brainfuckCustomEventWorkerExit, + brainfuckCustomEventByteInputDone, + brainfuckCustomEventTextInputDone, +}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +struct BFApp { + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Submenu* submenu; + Popup* popup; + TextInput* text_input; + TextBox* text_box; + FuriString* text_box_store; + FuriString* BF_file_path; + BFDevEnv* BF_dev_env; + int dataSize; + char dataBuffer[BF_INST_BUFFER_SIZE]; + char inputBuffer[BF_INPUT_BUFFER_SIZE]; +}; + +typedef enum { + brainfuckViewMenu, + brainfuckViewPopup, + brainfuckViewLoading, + brainfuckViewTextInput, + brainfuckViewTextBox, + brainfuckViewWidget, + brainfuckViewDev, + brainfuckViewExec, +} brainfuckView; diff --git a/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d5f87db1ca55141449cfcc3bf054417eaa84e1 GIT binary patch literal 1738 zcmcIl%Wm676lEHu%>zLLWYHwZf?zfc+Tjd`l=wgx!?E02K7gZd!)A>b-AnNYDb z=UD-0O?$90FBm_PwI0iHnuo^kzrHc_RD{NpUPPi|OHR_A(^5V@-5v4MBkl`h`li))oId$h zr-Twrdf1}K>IcLLELU%T22?9W66_DYYiq$%XiVz52r!<_X6DQ`RXN6%@B5fgOeq2c zsup?8<|wc3bqoVp@iHyyRONcZ$YOO|hXyEJO(84Rw0YIq1cu=`E3jpfW=b6}iq3{+ z*&1Ed+b2+^)%#K6YP2XM-j|g+F1g%3k$HWuD^^TYt*VLogtqnH|4=CSx?pi!PM7uw zj^$Klz+C~>TIwr;tx~dDl_RC5T~K>nMV(qE)xUm{=0eS?`;DS@fE=(|h6bc&Ap((k zBdZr!eqejwSR^211&yE&1gqKkz)Gaa;ylnO3Wj-Avz*J}AT&UfnWiG(Hm6?C6^L<1 zqL@1bP64XW zqKrwxT^V~c?$~}TQ}}Y&^h4H0l>o-Xk=)_jK{NqDuJ0r$_IQFAaV$sJiQn_7p}()Y zrKYNklmK^aLl-wVr`&ul_BH)&R_4UgD(ZOFr}nOx%X#N!gVeJ4h)=Wyh? zQXrf4V!h1m}&`|!B-3a0&FC= whJ($~<(FI>9{%<2-NwbcKNmOR7sY+;Hox}g7dMW6Yj&IA_U_=9M~Bb;1{rl65&!@I literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..c79cfb6c61feb86ebd442c0ed12f4b64f54fad54 GIT binary patch literal 1977 zcmcIlPmkL~6n8~;E4ErxK@Syhu)IBh%Gfjh8(T@MY_jY|s+VY+s>`jD@i?(I@fh2? z$u1HH-~bYW8wkWF0I3oeB;bHV#g$9H09Q_Y0mSlbCuxwD?OurFjOY2y@BRC|c^`Lo zx7XKRUXvtgy|dHagY^bHS1&&Y|6jb){Tr4`+1~c1^ys%&|A5WqLDO$a(s!R-{fn-^ zezkkIw{?1Y3a|HndGa1C&nG)?X8`e1@m!L=`WgVQtVF&2eBZz8F)>zYAo^ULjuX(9 zr23s{LfHY&))cu^$qyPzv#==d`(3#uQZCynMhs(Jc3hR%rfK6hWF6~R z!`oEh~hv@veyfbW(Rni=DeXm z^$5Z`!lXw`Psg5FyG5{vai!`59|D@wiz$mB3Xdib?N&T<+v-f1@!a7vA$z+;YK z5sTIkCmbUZm>9XlaFJ~}w(dBVV}zvi3(mI%J1jC$oUiB`2nHC7YxGqH)7?S=$1 z%OIAc$WQII#7LyR2q3%0vb@u3byE>WBlu9<4030yWfRx7k*-2m0rNtUjwui=8q)#S zl6WxZKCmCWfv9()3`%`m_EptnWp~};H1T|2wL^{2d=bF&FuQ?MXg%2dgMO*&W-Em&~-?Dnv0yx+GSj;J1TEH zrrxbiyV<+(ggw08mR6hQ3mY$POJ6>gR^(5mPac1ye(=qeU(K8EUQ@4Km!#jV)gM0m S{KuQcwCrr{wjXUieE%Qfkx0w{ literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..00e66428d76e298f0c2c784e1fad66b98d832b8a GIT binary patch literal 1979 zcmcIl&2Jk;6yK<6DpJCQ9tu=wwY`8~yfgdhUD+*;Z5m76V5P1aZW+(c+N;<*+ucp< zM2JIgNJ#XCg!l_Us>B6Fa6sb3iA(+pBqYRvTSa)gYr94YX)ah=&(7O7zxVZf^FHft z-`iM!b6t|8jm}nU2iBYLTwlEc|6jh<{Tr5*bm!it^z7GLzrkj8(C`|P^xYTNpVOB!4C3F%ToBi#Ebo`P!g8W;Uzv%+b1HDT zSWDQ+kOSxi1S-{15o8mZ@WueD^>Tu$mk1=KhZl!&IU@a^VU*ZxNTUIV8yQ-4Ik1|V zUtY^qtEuM~xb}iqb8a@EYjPH(OPMNIut^6?q6G`?oZlW+mG}FW%@NfCjvTXS4^2l^ zN;T|gRa@)x($7)21Y3%hP1V$w`&gb@y1fg;QVOF1c94MiT*`Ng*~TMCUjmcs+q3T< z!qh{ZR-)xFint52(+|8nc(HThcfMEc>?Z{EIf`t9YlHa)U U{I8QgKgs81r@h^Jwz>D@AE;?dx&QzG literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png b/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png new file mode 100644 index 0000000000000000000000000000000000000000..4c04a08566958747a8977da87a884723241f8ad2 GIT binary patch literal 1992 zcmcIl&5zqe6!)s#!fv&vUJ62Bxg4lq>=}Q?R?;e)EZs=!CEBLy!UZPwII%YI7~8wa z?x9GnIB-BcajEzZkcuiq#mAwv7sRDPs?;8P=ERW;((-I4X^@ufUWnw3=lRX=ef{3N zFWQ?|R+e9176f6Xwcgx<^-Xxro<0TtcNWDTVOdDGuB-}oAHVSjY)<#;Ze0-W-9Gy_ zU4;Fa_C{yz@bC~`uRVHl1D2(D{lgR>&gIX7aOdmug7C^>*xAmu-3`a%Lz()#%jEGe z25mv8ULMEP+hLj5WxX(}Nq_$QKoUd0CS5mN?8Xh&57!S8cJ*Mh;~ngHwl7`2w6a_s zJAg1`85PIFL6kb-HQ%3@gI!JPXIbnh%4jr_N1Du&otcVdV2sdNKECa)32y z#%dB&k07ifOgcn&RP5-LcL{bdE>&INgHJPhGG*?E!Tw29UDs(vX+|TDwVE{voRY)P zcRWioh(Rle5rz@*b&PDH*~m03Q?)F^(gITW1?QXG+s!kPpD*d_a}O|P*>p7KRhX}L zQJ`yGWL0z@b#;v*tP|pUR5MLOpQ3$`gwR5CFprvhxRIe% z=L4%M`Q-dS`1M&V8}L# z_E0rcp;X1TTCvnFEBzdVbFigo-c(h2xsT0UlOTWg!myQ`nv{0Aa^SG)iK literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyInput_30x11.png b/applications/plugins/brainfuck/icons/KeyInput_30x11.png new file mode 100644 index 0000000000000000000000000000000000000000..d23e24aaf79f293d10680c29a582ecb0fbecd6ed GIT binary patch literal 1994 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png b/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb3569d3accc5a5c56829b12c85079172b56729 GIT binary patch literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeySave_24x11.png b/applications/plugins/brainfuck/icons/KeySave_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..e7dba987a04dad7dd96001913c55566dbde96c8e GIT binary patch literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/bfico.png b/applications/plugins/brainfuck/icons/bfico.png new file mode 100644 index 0000000000000000000000000000000000000000..b25368fb53e03b73878a9aa17a044c2d8dffea4a GIT binary patch literal 1822 zcma)6eM}o=7(YOmKyZGbBNNT#*c_9!cReT{7dirMfhxy3z6O4=Yws<+(e}=}D=ip7*(DuemTUCTeXIf*>)bd_xg@E9&+&;9yN=+jdK75 zNgO}WHlAeI8at9@wST^cjE;Wo&AI%pY})Zgdviz1zO505+S5*dbUq^E9Q0s*?vo}AfY~MGw zu|Mzolpned^&i|}@9r|UW{rFobKix^-<~<~`{a9D*2O#=KX^8w_sE*&4b4qS;g_z_ z%>ItO>znt#TrbxizB$-4;%pp9ch2lSSGO*{^-4$cf!Wn9^V_xE*PDC9E7G@MaPIMA z4|>R~WYsWTmK4E!G2Km^j|*v-P2Ti%9v(J(z9sA0(ByRSCR5*&W!-zmFpiA<@d0Pp79Ia%{^SR_JA^Z1H*V=KHF3l8&g!tuPanB6%y+!KYUx`BAxQK?$`gWgbjBe_ zcqCU+E|=2M$QM1=orh$50Da& zB0#ljt(w4KIErS9tc@x%g0It(iCNdsPPiQFXx3II!iD;`{ux2YWdl}(wAWi=})ahK5Ey>0jm~fzeL6R4UrIK0! z;6*^<-J%s3Yk*5mz)+0+$5K3LV69PsLrH#lrZ> zXr6^!$r!q1hqEFuDs-4sl$V2=jQUhfMrx{xRKr7+N>L^!Qw0iut4g-}4_vZtbMH0)HCkr@LcL%}2g`%FWsNTP($QYj>* z(IycY6aiEBfq9m*)&3XM*WD=mWx^Pmrt&#S=Ed4YbD(F7!HdgG0i%Oc4uV09RY^^U z2_nzBt$_6jfOd(u$sR@o*;VlVbXc~{#=-P!QM1yws79?J)Ts+tj~zgU;G8rm6p_A= z6TYGVUoWj7z;FM50{qyO1OydzxqwVu`Er+6B@ + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) brainfuckScene##id, +typedef enum { +#include "brainfuck_scene_config.h" + brainfuckSceneNum, +} brainfuckScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers brainfuck_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "brainfuck_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 "brainfuck_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 "brainfuck_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h new file mode 100644 index 000000000..0efc41641 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(brainfuck, start, Start) +ADD_SCENE(brainfuck, file_select, FileSelect) +ADD_SCENE(brainfuck, file_create, FileCreate) +ADD_SCENE(brainfuck, dev_env, DevEnv) +ADD_SCENE(brainfuck, exec_env, ExecEnv) +ADD_SCENE(brainfuck, set_input, SetInput) \ No newline at end of file diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c new file mode 100644 index 000000000..475e9e573 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_dev_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewDev); +} + +bool brainfuck_scene_dev_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_dev_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c new file mode 100644 index 000000000..d344f7271 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_exec_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextBox); +} + +bool brainfuck_scene_exec_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_exec_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c new file mode 100644 index 000000000..9f8885977 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c @@ -0,0 +1,50 @@ +#include "../brainfuck_i.h" + +void file_name_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +char tmpName[64] = {}; +byte empty[1] = {0x00}; +void brainfuck_scene_file_create_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "New script name"); + text_input_set_result_callback( + text_input, file_name_text_input_callback, app, tmpName, 64, true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + UNUSED(app); + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName); + + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(app->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(app->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)empty, 1); + buffered_file_stream_close(stream); + + //scene_manager_next_scene(app->scene_manager, brainfuckSceneFileSelect); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_file_create_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c new file mode 100644 index 000000000..33c06ee81 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c @@ -0,0 +1,34 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_file_select_on_enter(void* context) { + BFApp* app = context; + + DialogsApp* dialogs = furi_record_open("dialogs"); + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, "/ext/brainfuck"); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico); + browser_options.base_path = "/ext/brainfuck"; + browser_options.hide_ext = false; + + bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options); + + if(selected) { + furi_string_set(app->BF_file_path, path); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } else { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneStart); + } +} + +bool brainfuck_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c new file mode 100644 index 000000000..efb9237cb --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c @@ -0,0 +1,35 @@ +#include "../brainfuck_i.h" + +void set_input_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +void brainfuck_scene_set_input_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Edit input buffer"); + text_input_set_result_callback( + text_input, set_input_text_input_callback, app, app->inputBuffer, 64, true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_set_input_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_set_input_on_exit(void* context) { + BFApp* app = context; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c new file mode 100644 index 000000000..8eaaf751a --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c @@ -0,0 +1,55 @@ +#include "../brainfuck_i.h" +enum SubmenuIndex { + SubmenuIndexNew, + SubmenuIndexOpen, + SubmenuIndexAbout, +}; + +void brainfuck_scene_start_submenu_callback(void* context, uint32_t index) { + BFApp* brainfuck = context; + view_dispatcher_send_custom_event(brainfuck->view_dispatcher, index); +} +void brainfuck_scene_start_on_enter(void* context) { + BFApp* brainfuck = context; + + Submenu* submenu = brainfuck->submenu; + submenu_add_item( + submenu, "New", SubmenuIndexNew, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item( + submenu, "Open", SubmenuIndexOpen, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item( + submenu, "About", SubmenuIndexAbout, brainfuck_scene_start_submenu_callback, brainfuck); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(brainfuck->scene_manager, brainfuckSceneStart)); + view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewMenu); +} + +bool brainfuck_scene_start_on_event(void* context, SceneManagerEvent event) { + BFApp* brainfuck = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNew) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileCreate); + consumed = true; + } else if(event.event == SubmenuIndexOpen) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexAbout) { + text_box_set_text( + brainfuck->text_box, + "FlipperBrainfuck\n\nAn F0 brainfuck intepretor\nBy github.com/Nymda"); + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneExecEnv); + consumed = true; + } + scene_manager_set_scene_state(brainfuck->scene_manager, brainfuckSceneStart, event.event); + } + + return consumed; +} + +void brainfuck_scene_start_on_exit(void* context) { + BFApp* brainfuck = context; + submenu_reset(brainfuck->submenu); +} diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c new file mode 100644 index 000000000..c5f194500 --- /dev/null +++ b/applications/plugins/brainfuck/views/bf_dev_env.c @@ -0,0 +1,419 @@ +#include "bf_dev_env.h" +#include + +typedef struct BFDevEnv { + View* view; + DevEnvOkCallback callback; + void* context; + BFApp* appDev; +} BFDevEnv; + +typedef struct { + uint32_t row; + uint32_t col; +} BFDevEnvModel; + +typedef struct { + int up; + int down; + int left; + int right; +} bMapping; + +static bool bf_dev_process_up(BFDevEnv* devEnv); +static bool bf_dev_process_down(BFDevEnv* devEnv); +static bool bf_dev_process_left(BFDevEnv* devEnv); +static bool bf_dev_process_right(BFDevEnv* devEnv); +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event); + +BFApp* appDev; +FuriThread* workerThread; + +char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00}; + +int selectedButton = 0; +int saveNotifyCountdown = 0; +int execCountdown = 0; + +char dspLine0[25] = {}; +char dspLine1[25] = {}; +char dspLine2[25] = {}; + +static bMapping buttonMappings[12] = { + {8, 8, 7, 1}, //0 + {8, 8, 0, 2}, //1 + {9, 9, 1, 3}, //2 + {9, 9, 2, 4}, //3 + {10, 10, 3, 5}, //4 + {10, 10, 4, 6}, //5 + {11, 11, 5, 7}, //6 + {11, 11, 6, 0}, //7 + + {0, 0, 11, 9}, //8 + {3, 3, 8, 10}, //9 + {5, 5, 9, 11}, //10 + {6, 6, 10, 8} //11 +}; + +#define BT_X 14 +#define BT_Y 14 +static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) { + UNUSED(lbl); + + if(selected) { + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); + canvas_invert_color(canvas); + } else { + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3); + canvas_invert_color(canvas); + canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); + } +} + +void bf_save_changes() { + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); +} + +static void bf_dev_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + if(saveNotifyCountdown > 0) { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED"); + saveNotifyCountdown--; + return; + } + + bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0 + bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1 + bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2 + bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3 + bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0 + bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1 + bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2 + bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3 + + //backspace, input, run, save + canvas_draw_icon( + canvas, + 1, + 52, + (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11); + canvas_draw_icon( + canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11); + canvas_draw_icon( + canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11); + canvas_draw_icon( + canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11); + + if(saveNotifyCountdown > 0) { + canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5); + saveNotifyCountdown--; + } + + //textbox + //grossly overcomplicated. not fixing it. + canvas_draw_rframe(canvas, 1, 1, 126, 33, 2); + canvas_set_font(canvas, FontBatteryPercent); + + int dbOffset = 0; + if(appDev->dataSize > 72) { + dbOffset = (appDev->dataSize - 72); + } + + memset(dspLine0, 0x00, 25); + memset(dspLine1, 0x00, 25); + memset(dspLine2, 0x00, 25); + + int tpM = 0; + int tp0 = 0; + int tp1 = 0; + int tp2 = 0; + + for(int p = dbOffset; p < appDev->dataSize; p++) { + if(tpM < 24 * 1) { + dspLine0[tp0] = appDev->dataBuffer[p]; + tp0++; + } else if(tpM < 24 * 2) { + dspLine1[tp1] = appDev->dataBuffer[p]; + tp1++; + } else if(tpM < 24 * 3) { + dspLine2[tp2] = appDev->dataBuffer[p]; + tp2++; + } + tpM++; + } + + canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0); + canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1); + canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2); +} + +static bool bf_dev_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = bf_dev_process_right(devEnv); + } else if(event->key == InputKeyLeft) { + consumed = bf_dev_process_left(devEnv); + } else if(event->key == InputKeyUp) { + consumed = bf_dev_process_up(devEnv); + } else if(event->key == InputKeyDown) { + consumed = bf_dev_process_down(devEnv); + } + } else if(event->key == InputKeyOk) { + consumed = bf_dev_process_ok(devEnv, event); + } + + return consumed; +} + +static bool bf_dev_process_up(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].up; + return true; +} + +static bool bf_dev_process_down(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].down; + return true; +} + +static bool bf_dev_process_left(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].left; + return true; +} + +static bool bf_dev_process_right(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].right; + return true; +} + +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) { + UNUSED(devEnv); + UNUSED(event); + + if(event->type != InputTypePress) { + return false; + } + + switch(selectedButton) { + case 0: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '+'; + appDev->dataSize++; + } + break; + } + + case 1: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '-'; + appDev->dataSize++; + } + break; + } + + case 2: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '<'; + appDev->dataSize++; + } + break; + } + + case 3: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '>'; + appDev->dataSize++; + } + break; + } + + case 4: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '['; + appDev->dataSize++; + } + break; + } + + case 5: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = ']'; + appDev->dataSize++; + } + break; + } + + case 6: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '.'; + appDev->dataSize++; + } + break; + } + + case 7: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = ','; + appDev->dataSize++; + } + break; + } + + case 8: { + if(appDev->dataSize > 0) { + appDev->dataSize--; + appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00; + } + break; + } + + case 9: { + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput); + break; + } + + case 10: { + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + bf_save_changes(); + + initWorker(appDev); + text_box_set_focus(appDev->text_box, TextBoxFocusEnd); + text_box_set_text(appDev->text_box, workerGetOutput()); + + workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL); + furi_thread_start(workerThread); + + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv); + break; + } + + case 11: { + bf_save_changes(); + saveNotifyCountdown = 3; + break; + } + } + + bool consumed = false; + return consumed; +} + +static void bf_dev_enter_callback(void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + + with_view_model( + devEnv->view, + BFDevEnvModel * model, + { + model->col = 0; + model->row = 0; + }, + true); + + appDev = devEnv->appDev; + selectedButton = 0; + + //exit the running thread if required + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + //clear the bf instruction buffer + memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char)); + + //open the file + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING); + + //read into the buffer + appDev->dataSize = stream_size(stream); + stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); + + //replaces any invalid characters with an underscore. strips out newlines, comments, etc + for(int i = 0; i < appDev->dataSize; i++) { + if(!strchr(bfChars, appDev->dataBuffer[i])) { + appDev->dataBuffer[i] = '_'; + } + } + + //find the end of the file to begin editing + int tptr = 0; + while(appDev->dataBuffer[tptr] != 0x00) { + tptr++; + } + appDev->dataSize = tptr; +} + +BFDevEnv* bf_dev_env_alloc(BFApp* appDev) { + BFDevEnv* devEnv = malloc(sizeof(BFDevEnv)); + + devEnv->view = view_alloc(); + devEnv->appDev = appDev; + view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel)); + + with_view_model( + devEnv->view, + BFDevEnvModel * model, + { + model->col = 0; + model->row = 0; + }, + true); + + view_set_context(devEnv->view, devEnv); + view_set_draw_callback(devEnv->view, bf_dev_draw_callback); + view_set_input_callback(devEnv->view, bf_dev_input_callback); + view_set_enter_callback(devEnv->view, bf_dev_enter_callback); + return devEnv; +} + +void bf_dev_env_free(BFDevEnv* devEnv) { + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + furi_assert(devEnv); + view_free(devEnv->view); + free(devEnv); +} + +View* bf_dev_env_get_view(BFDevEnv* devEnv) { + furi_assert(devEnv); + return devEnv->view; +} diff --git a/applications/plugins/brainfuck/views/bf_dev_env.h b/applications/plugins/brainfuck/views/bf_dev_env.h new file mode 100644 index 000000000..31059544b --- /dev/null +++ b/applications/plugins/brainfuck/views/bf_dev_env.h @@ -0,0 +1,15 @@ +#pragma once +#include "../brainfuck_i.h" +#include + +typedef void (*DevEnvOkCallback)(InputType type, void* context); + +BFDevEnv* bf_dev_env_alloc(BFApp* application); + +void bf_dev_set_file_path(FuriString* path); + +void bf_dev_env_free(BFDevEnv* devEnv); + +View* bf_dev_env_get_view(BFDevEnv* devEnv); + +void bf_dev_env_set_ok(BFDevEnv* devEnv, DevEnvOkCallback callback, void* context); diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c new file mode 100644 index 000000000..1b05ac3fd --- /dev/null +++ b/applications/plugins/brainfuck/worker.c @@ -0,0 +1,276 @@ +#include "worker.h" + +bool killswitch = false; + +int status = 0; //0: idle, 1: running, 2: failure + +char* inst = 0; +int instCount = 0; +int instPtr = 0; +int runOpCount = 0; + +char* wOutput = 0; +int wOutputPtr = 0; + +char* wInput = 0; +int wInputPtr = 0; + +uint8_t* bfStack = 0; +int stackPtr = 0; +int stackSize = BF_STACK_INITIAL_SIZE; +int stackSizeReal = 0; + +BFApp* wrkrApp = 0; + +void killThread() { + killswitch = true; +} + +bool validateInstPtr() { + if(instPtr > instCount || instPtr < 0) { + return false; + } + return true; +} + +bool validateStackPtr() { + if(stackPtr > stackSize || stackPtr < 0) { + return false; + } + return true; +} + +char* workerGetOutput() { + return wOutput; +} + +int getStackSize() { + return stackSizeReal; +} + +int getOpCount() { + return runOpCount; +} + +int getStatus() { + return status; +} + +void initWorker(BFApp* app) { + wrkrApp = app; + + //rebuild output + if(wOutput) { + free(wOutput); + } + wOutput = (char*)malloc(BF_OUTPUT_SIZE); + wOutputPtr = 0; + + //rebuild stack + if(bfStack) { + free(bfStack); + } + bfStack = (uint8_t*)malloc(BF_STACK_INITIAL_SIZE); + memset(bfStack, 0x00, BF_STACK_INITIAL_SIZE); + stackSize = BF_STACK_INITIAL_SIZE; + stackSizeReal = 0; + stackPtr = 0; + + //set instructions + inst = wrkrApp->dataBuffer; + instCount = wrkrApp->dataSize; + instPtr = 0; + runOpCount = 0; + + //set input + wInput = wrkrApp->inputBuffer; + wInputPtr = 0; + + //set status + status = 0; +} + +void rShift() { + runOpCount++; + stackPtr++; + if(!validateStackPtr()) { + status = 2; + return; + } + + while(stackPtr > stackSize) { + stackSize += BF_STACK_STEP_SIZE; + void* tmp = realloc(bfStack, stackSize); + + if(!tmp) { + status = 2; + return; + } + + memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE); + bfStack = (uint8_t*)tmp; + }; + if(stackPtr > stackSizeReal) { + stackSizeReal = stackPtr; + } +} + +void lShift() { + runOpCount++; + stackPtr--; + if(!validateStackPtr()) { + status = 2; + return; + } +} + +void inc() { + runOpCount++; + if(!validateStackPtr()) { + status = 2; + return; + } + bfStack[stackPtr]++; +} + +void dec() { + runOpCount++; + if(!validateStackPtr()) { + status = 2; + return; + } + bfStack[stackPtr]--; +} + +void print() { + runOpCount++; + wOutput[wOutputPtr] = bfStack[stackPtr]; + wOutputPtr++; + if(wOutputPtr > (BF_OUTPUT_SIZE - 1)) { + wOutputPtr = 0; + } +} + +void input() { + runOpCount++; + + bfStack[stackPtr] = (uint8_t)wInput[wInputPtr]; + if(wInput[wInputPtr] == 0x00 || wInputPtr >= 64) { + wInputPtr = 0; + } else { + wInputPtr++; + } +} + +void loop() { + runOpCount++; + if(bfStack[stackPtr] == 0) { + int loopCount = 1; + while(loopCount > 0) { + instPtr++; + if(!validateInstPtr()) { + status = 2; + return; + } + if(inst[instPtr] == '[') { + loopCount++; + } else if(inst[instPtr] == ']') { + loopCount--; + } + } + } +} + +void endLoop() { + runOpCount++; + if(bfStack[stackPtr] != 0) { + int loopCount = 1; + while(loopCount > 0) { + instPtr--; + if(!validateInstPtr()) { + status = 2; + return; + } + if(inst[instPtr] == ']') { + loopCount++; + } else if(inst[instPtr] == '[') { + loopCount--; + } + } + } +} + +static const NotificationSequence led_on = { + &message_blue_255, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_off = { + &message_green_0, + NULL, +}; + +void beginWorker() { + status = 1; + while(inst[instPtr] != 0x00) { + if(runOpCount % 500 == 0) { + text_box_set_text(wrkrApp->text_box, workerGetOutput()); + notification_message(wrkrApp->notifications, &led_on); + } + + if(status == 2) { + status = 0; + break; + } + if(killswitch) { + status = 0; + killswitch = false; + break; + } + switch(inst[instPtr]) { + case '>': + rShift(); + break; + case '<': + lShift(); + break; + + case '+': + inc(); + break; + + case '-': + dec(); + break; + + case '.': + print(); + break; + + case ',': + input(); + break; + + case '[': + loop(); + break; + + case ']': + endLoop(); + break; + + default: + break; + } + instPtr++; + if(!validateInstPtr()) { + status = 0; + break; + } + } + + notification_message(wrkrApp->notifications, &led_off); + text_box_set_text(wrkrApp->text_box, workerGetOutput()); + status = 0; +} \ No newline at end of file diff --git a/applications/plugins/brainfuck/worker.h b/applications/plugins/brainfuck/worker.h new file mode 100644 index 000000000..b12e364c3 --- /dev/null +++ b/applications/plugins/brainfuck/worker.h @@ -0,0 +1,9 @@ +#include "brainfuck_i.h" + +void initWorker(BFApp* application); +char* workerGetOutput(); +int getStackSize(); +int getOpCount(); +int getStatus(); +void beginWorker(); +void killThread(); \ No newline at end of file diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam new file mode 100644 index 000000000..9fddb52b0 --- /dev/null +++ b/applications/plugins/geigercounter/application.fam @@ -0,0 +1,13 @@ +App( + appid="Geiger_Coutner", + name="Geiger Counter", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipper_geiger_app", + cdefines=["APP_GEIGER"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="geiger.png", + fap_category="GPIO", +) diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/plugins/geigercounter/flipper_geiger.c new file mode 100644 index 000000000..709db9a26 --- /dev/null +++ b/applications/plugins/geigercounter/flipper_geiger.c @@ -0,0 +1,227 @@ +// CC0 1.0 Universal (CC0 1.0) +// Public Domain Dedication +// https://github.com/nmrr + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_SIZE_X 128 +#define SCREEN_SIZE_Y 64 + +// FOR J305 GEIGER TUBE +#define CONVERSION_FACTOR 0.0081 + +typedef enum { + EventTypeInput, + ClockEventTypeTick, + EventGPIO, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} EventApp; + +typedef struct { + uint32_t cps, cpm; + uint32_t line[SCREEN_SIZE_X / 2]; + float coef; + uint8_t data; +} mutexStruct; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + mutexStruct displayStruct; + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx); + memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); + release_mutex((ValueMutex*)ctx, geigerMutex); + + char buffer[32]; + if(displayStruct.data == 0) + snprintf( + buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); + else if(displayStruct.data == 1) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f uSv/h", + displayStruct.cps, + ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); + else + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f mSv/y", + displayStruct.cps, + (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + + for(int i = 0; i < SCREEN_SIZE_X; i += 2) { + float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); + + canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); + canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + EventApp event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void clock_tick(void* ctx) { + furi_assert(ctx); + + uint32_t randomNumber = furi_hal_random_get(); + randomNumber &= 0xFFF; + if(randomNumber == 0) randomNumber = 1; + + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); + + FuriMessageQueue* queue = ctx; + EventApp event = {.type = ClockEventTypeTick}; + furi_message_queue_put(queue, &event, 0); +} + +static void gpiocallback(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* queue = ctx; + EventApp event = {.type = EventGPIO}; + furi_message_queue_put(queue, &event, 0); +} + +int32_t flipper_geiger_app() { + EventApp event; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); + + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInterruptFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 5, 50); + + mutexStruct mutexVal; + mutexVal.cps = 0; + mutexVal.cpm = 0; + for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0; + mutexVal.coef = 1; + mutexVal.data = 0; + + uint32_t counter = 0; + + ValueMutex state_mutex; + init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal)); + + 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); + + furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 1000); + + // ENABLE 5V pin + furi_hal_power_enable_otg(); + + while(1) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); + + uint8_t screenRefresh = 0; + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + break; + } else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) { + counter = 0; + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + geigerMutex->cps = 0; + geigerMutex->cpm = 0; + for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if((event.input.key == InputKeyLeft && + event.input.type == InputTypeShort)) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + if(geigerMutex->data != 0) + geigerMutex->data--; + else + geigerMutex->data = 2; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if((event.input.key == InputKeyRight && + event.input.type == InputTypeShort)) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + if(geigerMutex->data != 2) + geigerMutex->data++; + else + geigerMutex->data = 0; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } + } else if(event.type == ClockEventTypeTick) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++) + geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] = + geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i]; + + geigerMutex->line[0] = counter; + geigerMutex->cps = counter; + counter = 0; + + geigerMutex->cpm = geigerMutex->line[0]; + uint32_t max = geigerMutex->line[0]; + for(int i = 1; i < SCREEN_SIZE_X / 2; i++) { + if(i < 60) geigerMutex->cpm += geigerMutex->line[i]; + if(geigerMutex->line[i] > max) max = geigerMutex->line[i]; + } + + if(max > 0) + geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max); + else + geigerMutex->coef = 1; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if(event.type == EventGPIO) { + counter++; + } + } + + if(screenRefresh == 1) view_port_update(view_port); + } + + furi_hal_power_disable_otg(); + + furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); + furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_timer_free(timer); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/geigercounter/geiger.png b/applications/plugins/geigercounter/geiger.png new file mode 100644 index 0000000000000000000000000000000000000000..d41e1915b6821ab19f982f1c99cad821d5a38abe GIT binary patch literal 8048 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 +#include + +#include +#include + +#include +#include + +#include "clock_app.h" + +/* + This is a modified version of the default clock app intended for use overnight + Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off. +*/ + +int brightness = 5; +bool led = false; +NotificationApp* notif = 0; + +const NotificationMessage message_red_dim = { + .type = NotificationMessageTypeLedRed, + .data.led.value = 0xFF / 16, +}; + +const NotificationMessage message_red_off = { + .type = NotificationMessageTypeLedRed, + .data.led.value = 0x00, +}; + +static const NotificationSequence led_on = { + &message_red_dim, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_off = { + &message_red_off, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_reset = { + &message_red_0, + NULL, +}; + +void set_backlight_brightness(float brightness) { + notif->settings.display_brightness = brightness; + notification_message(notif, &sequence_display_backlight_on); +} + +void handle_up() { + if(brightness < 100) { + led = false; + notification_message(notif, &led_off); + brightness += 5; + } + set_backlight_brightness((float)(brightness / 100.f)); +} + +void handle_down() { + if(brightness > 0) { + brightness -= 5; + if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition + led = true; + notification_message(notif, &led_on); + } + } else if(brightness == 0) { //trigger on every down press afterwards + led = !led; + if(led) { + notification_message(notif, &led_on); + } else { + notification_message(notif, &led_off); + } + } + set_backlight_brightness((float)(brightness / 100.f)); +} + +static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void clock_render_callback(Canvas* const canvas, void* ctx) { + //canvas_clear(canvas); + //canvas_set_color(canvas, ColorBlack); + + //avoids a bug with the brightness being reverted after the backlight-off period + set_backlight_brightness((float)(brightness / 100.f)); + + ClockState* state = ctx; + if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) { + //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render"); + PluginEvent event = {.type = EventTypeTick}; + furi_message_queue_put(state->event_queue, &event, 0); + return; + } + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + char time_string[TIME_LEN]; + char date_string[DATE_LEN]; + char meridian_string[MERIDIAN_LEN]; + char timer_string[20]; + + if(state->time_format == LocaleTimeFormat24h) { + snprintf( + time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second); + } else { + bool pm = curr_dt.hour > 12; + bool pm12 = curr_dt.hour >= 12; + snprintf( + time_string, + TIME_LEN, + CLOCK_TIME_FORMAT, + pm ? curr_dt.hour - 12 : curr_dt.hour, + curr_dt.minute, + curr_dt.second); + + snprintf( + meridian_string, + MERIDIAN_LEN, + MERIDIAN_FORMAT, + pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM); + } + + if(state->date_format == LocaleDateFormatYMD) { + snprintf( + date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day); + } else if(state->date_format == LocaleDateFormatMDY) { + snprintf( + date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year); + } else { + snprintf( + date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year); + } + + bool timer_running = state->timer_running; + uint32_t timer_start_timestamp = state->timer_start_timestamp; + uint32_t timer_stopped_seconds = state->timer_stopped_seconds; + + furi_mutex_release(state->mutex); + + canvas_set_font(canvas, FontBigNumbers); + + if(timer_start_timestamp != 0) { + int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) : + timer_stopped_seconds; + snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE + elements_button_left(canvas, "Reset"); + } else { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, time_string); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 65, 17, AlignCenter, AlignCenter, date_string); + + if(state->time_format == LocaleTimeFormat12h) + canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignCenter, meridian_string); + } + if(timer_running) { + elements_button_center(canvas, "Stop"); + } else if(timer_start_timestamp != 0 && !timer_running) { + elements_button_center(canvas, "Start"); + } +} + +static void clock_state_init(ClockState* const state) { + state->time_format = locale_get_time_format(); + + state->date_format = locale_get_date_format(); + + //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h"); + //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322"); + //furi_hal_rtc_get_datetime(&state->datetime); +} + +// Runs every 1000ms by default +static void clock_tick(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + PluginEvent event = {.type = EventTypeTick}; + // It's OK to loose this event if system overloaded + furi_message_queue_put(event_queue, &event, 0); +} + +void timer_start_stop(ClockState* plugin_state) { + // START/STOP TIMER + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + if(plugin_state->timer_running) { + // Update stopped seconds + plugin_state->timer_stopped_seconds = curr_ts - plugin_state->timer_start_timestamp; + } else { + if(plugin_state->timer_start_timestamp == 0) { + // Set starting timestamp if this is first time + plugin_state->timer_start_timestamp = curr_ts; + } else { + // Timer was already running, need to slightly readjust so we don't + // count the intervening time + plugin_state->timer_start_timestamp = curr_ts - plugin_state->timer_stopped_seconds; + } + } + plugin_state->timer_running = !plugin_state->timer_running; +} + +void timer_reset_seconds(ClockState* plugin_state) { + if(plugin_state->timer_start_timestamp != 0) { + // Reset seconds + plugin_state->timer_running = false; + plugin_state->timer_start_timestamp = 0; + plugin_state->timer_stopped_seconds = 0; + } +} + +int32_t clock_app(void* p) { + UNUSED(p); + ClockState* plugin_state = malloc(sizeof(ClockState)); + + plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + if(plugin_state->event_queue == NULL) { + FURI_LOG_E(TAG, "Cannot create event queue"); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Event queue created"); + + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(plugin_state->mutex == NULL) { + FURI_LOG_E(TAG, "Cannot create mutex"); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Mutex created"); + + clock_state_init(plugin_state); + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, clock_render_callback, plugin_state); + view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue); + + FuriTimer* timer = + furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue); + + if(timer == NULL) { + FURI_LOG_E(TAG, "Cannot create timer"); + furi_mutex_free(plugin_state->mutex); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Timer created"); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + furi_timer_start(timer, furi_kernel_get_tick_frequency()); + //FURI_LOG_D(TAG, "Timer started"); + + notif = furi_record_open(RECORD_NOTIFICATION); + float tmpBrightness = notif->settings.display_brightness; + + notification_message(notif, &sequence_display_backlight_enforce_on); + notification_message(notif, &led_off); + + // Main loop + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100); + + if(event_status != FuriStatusOk) continue; + + if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue; + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyLeft: + // Reset seconds + timer_reset_seconds(plugin_state); + break; + case InputKeyOk: + // Toggle timer + timer_start_stop(plugin_state); + break; + case InputKeyBack: + // Exit the plugin + processing = false; + break; + default: + break; + } + } else if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + handle_up(); + break; + case InputKeyDown: + handle_down(); + break; + default: + break; + } + } + } /*else if(event.type == EventTypeTick) { + furi_hal_rtc_get_datetime(&plugin_state->datetime); + }*/ + + view_port_update(view_port); + furi_mutex_release(plugin_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(plugin_state->event_queue); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); + + set_backlight_brightness(tmpBrightness); + notification_message(notif, &sequence_display_backlight_enforce_auto); + notification_message(notif, &led_reset); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/nightstand/clock_app.h b/applications/plugins/nightstand/clock_app.h new file mode 100644 index 000000000..693bdfac0 --- /dev/null +++ b/applications/plugins/nightstand/clock_app.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#define TAG "Clock" + +#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d" +#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d" +#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d" + +#define MERIDIAN_FORMAT "%s" +#define MERIDIAN_STRING_AM "AM" +#define MERIDIAN_STRING_PM "PM" + +#define TIME_LEN 12 +#define DATE_LEN 14 +#define MERIDIAN_LEN 3 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + LocaleDateFormat date_format; + LocaleTimeFormat time_format; + FuriHalRtcDateTime datetime; + FuriMutex* mutex; + FuriMessageQueue* event_queue; + uint32_t timer_start_timestamp; + uint32_t timer_stopped_seconds; + bool timer_running; +} ClockState; diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam new file mode 100644 index 000000000..27e73a0ce --- /dev/null +++ b/applications/plugins/pomodoro/application.fam @@ -0,0 +1,12 @@ +App( + appid="Pomodoro2", + name="Pomodoro 2", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipp_pomodoro_app", + requires=["gui", "notification", "dolphin"], + stack_size=1 * 1024, + fap_category="Tools", + fap_icon_assets="images", + fap_icon="flipp_pomodoro_10.png", + fap_icon_assets_symbol="flipp_pomodoro", +) \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_10.png b/applications/plugins/pomodoro/flipp_pomodoro_10.png new file mode 100644 index 0000000000000000000000000000000000000000..977d16a584f63307ceb028a48f57713402abc520 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>3HNky4ABT~ zo#4oKK!Jm0_y2m6v$J|4wU)(+%sJ4oYSlc8Nnua6+`q%bGDG3H*bnyJge())*BAQu z?-*^XlzwJ?r0uexhV_H}+Gscene_manager); +}; + +static void flipp_pomodoro_app_tick_event_callback(void* ctx) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); +}; + +static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + switch(event) { + case FlippPomodoroAppCustomEventStageSkip: + flipp_pomodoro__toggle_stage(app->state); + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); + return CustomEventConsumed; + case FlippPomodoroAppCustomEventStageComplete: + if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) { + // REGISTER a deed on work stage complete to get an acheivement + DOLPHIN_DEED(DolphinDeedPluginGameWin); + }; + + flipp_pomodoro__toggle_stage(app->state); + notification_message( + app->notification_app, + stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]); + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); + return CustomEventConsumed; + default: + break; + } + return scene_manager_handle_custom_event(app->scene_manager, event); +}; + +FlippPomodoroApp* flipp_pomodoro_app_alloc() { + FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp)); + app->state = flipp_pomodoro__new(); + + app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app); + app->gui = furi_record_open(RECORD_GUI); + app->notification_app = furi_record_open(RECORD_NOTIFICATION); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, flipp_pomodoro_app_custom_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flipp_pomodoro_app_back_event_callback); + + app->timer_view = flipp_pomodoro_view_timer_alloc(); + + view_dispatcher_add_view( + app->view_dispatcher, + FlippPomodoroAppViewTimer, + flipp_pomodoro_view_timer_get_view(app->timer_view)); + + scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); + + return app; +}; + +void flipp_pomodoro_app_free(FlippPomodoroApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + flipp_pomodoro_view_timer_free(app->timer_view); + flipp_pomodoro__destroy(app->state); + free(app); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); +}; + +int32_t flipp_pomodoro_app(void* p) { + UNUSED(p); + FlippPomodoroApp* app = flipp_pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + flipp_pomodoro_app_free(app); + + return 0; +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.h b/applications/plugins/pomodoro/flipp_pomodoro_app.h new file mode 100644 index 000000000..54102e1f3 --- /dev/null +++ b/applications/plugins/pomodoro/flipp_pomodoro_app.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "views/flipp_pomodoro_timer_view.h" + +#include "modules/flipp_pomodoro.h" + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + FlippPomodoroAppCustomEventStageSkip = 100, + FlippPomodoroAppCustomEventStageComplete, // By Expiration + FlippPomodoroAppCustomEventTimerTick, + FlippPomodoroAppCustomEventStateUpdated, +} FlippPomodoroAppCustomEvent; + +typedef struct { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notification_app; + FlippPomodoroTimerView* timer_view; + FlippPomodoroState* state; +} FlippPomodoroApp; + +typedef enum { + FlippPomodoroAppViewTimer, +} FlippPomodoroAppView; \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h new file mode 100644 index 000000000..492ef9c2d --- /dev/null +++ b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h @@ -0,0 +1,31 @@ +#pragma once + +#define FURI_DEBUG 1 + +/** + * Index of dependencies for the main app + */ + +// Platform Imports + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// App resource imports + +#include "helpers/time.h" +#include "helpers/notifications.h" +#include "modules/flipp_pomodoro.h" +#include "flipp_pomodoro_app.h" +#include "scenes/flipp_pomodoro_scene.h" +#include "views/flipp_pomodoro_timer_view.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" diff --git a/applications/plugins/pomodoro/helpers/debug.h b/applications/plugins/pomodoro/helpers/debug.h new file mode 100644 index 000000000..13b8f2998 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/debug.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define TAG "FlippPomodoro" \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.c b/applications/plugins/pomodoro/helpers/notifications.c new file mode 100644 index 000000000..388a3f11d --- /dev/null +++ b/applications/plugins/pomodoro/helpers/notifications.c @@ -0,0 +1,49 @@ +#include + +const NotificationSequence work_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_b5, + &message_delay_250, + + &message_note_d5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_green_255, + &message_delay_1000, + &message_green_0, + &message_delay_250, + &message_green_255, + &message_delay_1000, + + NULL, +}; + +const NotificationSequence rest_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_d5, + &message_delay_250, + + &message_note_b5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_red_255, + &message_delay_1000, + &message_red_0, + &message_delay_250, + &message_red_255, + &message_delay_1000, + + NULL, +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.h b/applications/plugins/pomodoro/helpers/notifications.h new file mode 100644 index 000000000..c6cd0428f --- /dev/null +++ b/applications/plugins/pomodoro/helpers/notifications.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../modules/flipp_pomodoro.h" +#include + +extern const NotificationSequence work_start_notification; +extern const NotificationSequence rest_start_notification; + +/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage. +const NotificationSequence* stage_start_notification_sequence_map[] = { + [FlippPomodoroStageFocus] = &work_start_notification, + [FlippPomodoroStageRest] = &rest_start_notification, + [FlippPomodoroStageLongBreak] = &rest_start_notification, +}; diff --git a/applications/plugins/pomodoro/helpers/time.c b/applications/plugins/pomodoro/helpers/time.c new file mode 100644 index 000000000..7fb0d13c2 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/time.c @@ -0,0 +1,20 @@ +#include +#include +#include "time.h" + +const int TIME_SECONDS_IN_MINUTE = 60; +const int TIME_MINUTES_IN_HOUR = 60; + +uint32_t time_now() { + return furi_hal_rtc_get_timestamp(); +}; + +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { + const uint32_t duration_seconds = end - begin; + + uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR; + uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE; + + return ( + TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; +}; diff --git a/applications/plugins/pomodoro/helpers/time.h b/applications/plugins/pomodoro/helpers/time.h new file mode 100644 index 000000000..7a7d90bf2 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/time.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +extern const int TIME_SECONDS_IN_MINUTE; +extern const int TIME_MINUTES_IN_HOUR; + +/// @brief Container for a time period +typedef struct { + uint8_t seconds; + uint8_t minutes; + uint32_t total_seconds; +} TimeDifference; + +/// @brief Time by the moment of calling +/// @return A timestamp(seconds percision) +uint32_t time_now(); + +/// @brief Calculates difference between two provided timestamps +/// @param begin - start timestamp of the period +/// @param end - end timestamp of the period to measure +/// @return TimeDifference struct +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end); diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png b/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..7ead27c4e2d36b8155c088954b50e87cfc2a7e0c GIT binary patch literal 1242 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 +#include +#include "../helpers/time.h" +#include "flipp_pomodoro.h" + +PomodoroStage stages_sequence[] = { + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageLongBreak, +}; + +char* current_stage_label[] = { + [FlippPomodoroStageFocus] = "Continue focus for:", + [FlippPomodoroStageRest] = "Keep rest for:", + [FlippPomodoroStageLongBreak] = "Long Break for:", +}; + +char* next_stage_label[] = { + [FlippPomodoroStageFocus] = "Focus", + [FlippPomodoroStageRest] = "Short Break", + [FlippPomodoroStageLongBreak] = "Long Break", +}; + +PomodoroStage flipp_pomodoro__stage_by_index(int index) { + const int one_loop_size = sizeof(stages_sequence); + return stages_sequence[index % one_loop_size]; +} + +void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) { + furi_assert(state); + state->current_stage_index = state->current_stage_index + 1; + state->started_at_timestamp = time_now(); +}; + +PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) { + furi_assert(state); + return flipp_pomodoro__stage_by_index(state->current_stage_index); +}; + +char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) { + furi_assert(state); + return current_stage_label[flipp_pomodoro__get_stage(state)]; +}; + +char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { + furi_assert(state); + return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; +}; + +void flipp_pomodoro__destroy(FlippPomodoroState* state) { + furi_assert(state); + free(state); +}; + +uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { + const int32_t stage_duration_seconds_map[] = { + [FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE, + [FlippPomodoroStageRest] = 5 * TIME_SECONDS_IN_MINUTE, + [FlippPomodoroStageLongBreak] = 30 * TIME_SECONDS_IN_MINUTE, + }; + + return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)]; +}; + +uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) { + return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); +}; + +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) { + const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); + return time_difference_seconds(time_now(), stage_ends_at); +}; + +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) { + const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); + const uint8_t seamless_change_span_seconds = 1; + return (time_now() - seamless_change_span_seconds) >= expired_by; +}; + +FlippPomodoroState* flipp_pomodoro__new() { + FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState)); + const uint32_t now = time_now(); + state->started_at_timestamp = now; + state->current_stage_index = 0; + return state; +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.h b/applications/plugins/pomodoro/modules/flipp_pomodoro.h new file mode 100644 index 000000000..251a77469 --- /dev/null +++ b/applications/plugins/pomodoro/modules/flipp_pomodoro.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "../helpers/time.h" + +/// @brief Options of pomodoro stages +typedef enum { + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + FlippPomodoroStageLongBreak, +} PomodoroStage; + +/// @brief State of the pomodoro timer +typedef struct { + PomodoroStage stage; + uint8_t current_stage_index; + uint32_t started_at_timestamp; +} FlippPomodoroState; + +/// @brief Generates initial state +/// @returns A new pre-populated state for pomodoro timer +FlippPomodoroState* flipp_pomodoro__new(); + +/// @brief Extract current stage of pomodoro +/// @param state - pointer to the state of pomorodo +/// @returns Current stage value +PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state); + +/// @brief Destroys state of timer and it's dependencies +void flipp_pomodoro__destroy(FlippPomodoroState* state); + +/// @brief Get remaining stage time. +/// @param state - pointer to the state of pomorodo +/// @returns Time difference to the end of current stage +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state); + +/// @brief Label of currently active stage +/// @param state - pointer to the state of pomorodo +/// @returns A string that explains current stage +char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state); + +/// @brief Label of transition to the next stage +/// @param state - pointer to the state of pomorodo. +/// @returns string with the label of the "skipp" button +char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state); + +/// @brief Check if current stage is expired +/// @param state - pointer to the state of pomorodo. +/// @returns expriations status - true means stage is expired +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state); + +/// @brief Rotate stage of the timer +/// @param state - pointer to the state of pomorodo. +void flipp_pomodoro__toggle_stage(FlippPomodoroState* state); diff --git a/applications/plugins/pomodoro/scenes/.keep b/applications/plugins/pomodoro/scenes/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h b/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h new file mode 100644 index 000000000..f95daeb30 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(flipp_pomodoro, timer, Timer) \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c new file mode 100644 index 000000000..5856ac947 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c @@ -0,0 +1,30 @@ +#include "flipp_pomodoro_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = { +#include "config/flipp_pomodoro_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 flipp_pomodoro_scene_on_event_handlers[])(void* ctx, SceneManagerEvent event) = { +#include "config/flipp_pomodoro_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 flipp_pomodoro_scene_on_exit_handlers[])(void* ctx) = { +#include "config/flipp_pomodoro_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipp_pomodoro_scene_handlers = { + .on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers, + .on_event_handlers = flipp_pomodoro_scene_on_event_handlers, + .on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers, + .scene_num = FlippPomodoroSceneNum, +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h new file mode 100644 index 000000000..3b8a9052a --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h @@ -0,0 +1,27 @@ +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id, +typedef enum { +#include "config/flipp_pomodoro_scene_config.h" + FlippPomodoroSceneNum, +} FlippPomodoroScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipp_pomodoro_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "config/flipp_pomodoro_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* ctx, SceneManagerEvent event); +#include "config/flipp_pomodoro_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* ctx); +#include "config/flipp_pomodoro_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c new file mode 100644 index 000000000..2190dbdb7 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include "../flipp_pomodoro_app.h" +#include "../views/flipp_pomodoro_timer_view.h" + +enum { SceneEventConusmed = true, SceneEventNotConusmed = false }; + +uint8_t ExitSignal = 0; + +void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + flipp_pomodoro_view_timer_set_state( + flipp_pomodoro_view_timer_get_view(app->timer_view), app->state); +}; + +void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip); +}; + +void flipp_pomodoro_scene_timer_on_enter(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + flipp_pomodoro_scene_timer_sync_view_state(app); + flipp_pomodoro_view_timer_set_on_right_cb( + app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app); +}; + +void flipp_pomodoro_scene_timer_handle_custom_event( + FlippPomodoroApp* app, + FlippPomodoroAppCustomEvent custom_event) { + if(custom_event == FlippPomodoroAppCustomEventTimerTick && + flipp_pomodoro__is_stage_expired(app->state)) { + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStageComplete); + } + + if(custom_event == FlippPomodoroAppCustomEventStateUpdated) { + flipp_pomodoro_scene_timer_sync_view_state(app); + } +}; + +bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + switch(event.type) { + case SceneManagerEventTypeCustom: + flipp_pomodoro_scene_timer_handle_custom_event(app, event.event); + return SceneEventConusmed; + case SceneManagerEventTypeBack: + return ExitSignal; + default: + break; + }; + return SceneEventNotConusmed; +}; + +void flipp_pomodoro_scene_timer_on_exit(void* ctx) { + UNUSED(ctx); +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/.keep b/applications/plugins/pomodoro/views/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c new file mode 100644 index 000000000..e8e0383b7 --- /dev/null +++ b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c @@ -0,0 +1,195 @@ +#include "flipp_pomodoro_timer_view.h" +#include +#include +#include +#include +#include "../helpers/debug.h" +#include "../flipp_pomodoro_app.h" +#include "../modules/flipp_pomodoro.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" + +enum { + ViewInputConsumed = true, + ViewInputNotConusmed = false, +}; + +struct FlippPomodoroTimerView { + View* view; + FlippPomodoroTimerViewInputCb right_cb; + void* right_cb_ctx; +}; + +typedef struct { + IconAnimation* icon; + FlippPomodoroState* state; +} FlippPomodoroTimerViewModel; + +static const Icon* stage_background_image[] = { + [FlippPomodoroStageFocus] = &A_flipp_pomodoro_focus_64, + [FlippPomodoroStageRest] = &A_flipp_pomodoro_rest_64, + [FlippPomodoroStageLongBreak] = &A_flipp_pomodoro_rest_64, +}; + +static void + flipp_pomodoro_view_timer_draw_countdown(Canvas* canvas, TimeDifference remaining_time) { + canvas_set_font(canvas, FontBigNumbers); + const uint8_t right_border_margin = 1; + + const uint8_t countdown_box_height = canvas_height(canvas) * 0.4; + const uint8_t countdown_box_width = canvas_width(canvas) * 0.5; + const uint8_t countdown_box_x = + canvas_width(canvas) - countdown_box_width - right_border_margin; + const uint8_t countdown_box_y = 15; + + elements_bold_rounded_frame( + canvas, countdown_box_x, countdown_box_y, countdown_box_width, countdown_box_height); + + FuriString* timer_string = furi_string_alloc(); + furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds); + const char* remaining_stage_time_string = furi_string_get_cstr(timer_string); + canvas_draw_str_aligned( + canvas, + countdown_box_x + (countdown_box_width / 2), + countdown_box_y + (countdown_box_height / 2), + AlignCenter, + AlignCenter, + remaining_stage_time_string); + + furi_string_free(timer_string); +}; + +static void draw_str_with_drop_shadow( + Canvas* canvas, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical, + const char* str) { + canvas_set_color(canvas, ColorWhite); + for(int x_off = -2; x_off <= 2; x_off++) { + for(int y_off = -2; y_off <= 2; y_off++) { + canvas_draw_str_aligned(canvas, x + x_off, y + y_off, horizontal, vertical, str); + } + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, str); +} + +static void + flipp_pomodoro_view_timer_draw_current_stage_label(Canvas* canvas, FlippPomodoroState* state) { + canvas_set_font(canvas, FontPrimary); + draw_str_with_drop_shadow( + canvas, + canvas_width(canvas), + 0, + AlignRight, + AlignTop, + flipp_pomodoro__current_stage_label(state)); +} + +static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) { + if(!_model) { + return; + }; + + FlippPomodoroTimerViewModel* model = _model; + + canvas_clear(canvas); + if(model->icon) { + canvas_draw_icon_animation(canvas, 0, 0, model->icon); + } + + flipp_pomodoro_view_timer_draw_countdown( + canvas, flipp_pomodoro__stage_remaining_duration(model->state)); + + flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontSecondary); + elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); +}; + +bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { + furi_assert(ctx); + furi_assert(event); + FlippPomodoroTimerView* timer = ctx; + + const bool should_trigger_right_event_cb = (event->type == InputTypePress) && + (event->key == InputKeyRight) && + (timer->right_cb != NULL); + + if(should_trigger_right_event_cb) { + furi_assert(timer->right_cb); + furi_assert(timer->right_cb_ctx); + timer->right_cb(timer->right_cb_ctx); + return ViewInputConsumed; + }; + + return ViewInputNotConusmed; +}; + +View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) { + furi_assert(timer); + return timer->view; +}; + +void flipp_pomodoro_view_timer_assign_animation(View* view) { + with_view_model( + view, + FlippPomodoroTimerViewModel * model, + { + furi_assert(model->state); + if(model->icon) { + icon_animation_free(model->icon); + } + model->icon = icon_animation_alloc( + stage_background_image[flipp_pomodoro__get_stage(model->state)]); + view_tie_icon_animation(view, model->icon); + icon_animation_start(model->icon); + }, + true); +} + +FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() { + FlippPomodoroTimerView* timer = malloc(sizeof(FlippPomodoroTimerView)); + timer->view = view_alloc(); + + view_allocate_model(timer->view, ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel)); + view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer); + view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback); + view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); + + return timer; +}; + +void flipp_pomodoro_view_timer_set_on_right_cb( + FlippPomodoroTimerView* timer, + FlippPomodoroTimerViewInputCb right_cb, + void* right_cb_ctx) { + furi_assert(right_cb); + furi_assert(right_cb_ctx); + timer->right_cb = right_cb; + timer->right_cb_ctx = right_cb_ctx; +}; + +void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) { + furi_assert(view); + furi_assert(state); + with_view_model( + view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false); + flipp_pomodoro_view_timer_assign_animation(view); +}; + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { + furi_assert(timer); + with_view_model( + timer->view, + FlippPomodoroTimerViewModel * model, + { icon_animation_free(model->icon); }, + false); + view_free(timer->view); + + free(timer); +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h new file mode 100644 index 000000000..929a0eba3 --- /dev/null +++ b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "../modules/flipp_pomodoro.h" + +typedef struct FlippPomodoroTimerView FlippPomodoroTimerView; + +typedef void (*FlippPomodoroTimerViewInputCb)(void* ctx); + +FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc(); + +View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer); + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer); + +void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state); + +void flipp_pomodoro_view_timer_set_on_right_cb( + FlippPomodoroTimerView* timer, + FlippPomodoroTimerViewInputCb right_cb, + void* right_cb_ctx); diff --git a/applications/plugins/scrambler/LICENSE b/applications/plugins/scrambler/LICENSE new file mode 100644 index 000000000..4a6de25cb --- /dev/null +++ b/applications/plugins/scrambler/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 RaZe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/scrambler/README.md b/applications/plugins/scrambler/README.md new file mode 100644 index 000000000..7e4700bcd --- /dev/null +++ b/applications/plugins/scrambler/README.md @@ -0,0 +1,16 @@ +# Setting up the Rubik's Cube Scrambler + +## Installation +To install the Rubik's Cube Scrambler, simply add the `rubiks_cube_scrambler` folder to your `application_users` folder. + +## Cleaning the code and removing old files +Run `./fbt -c fap_rubiks_cube_scrambler` to clean the code and remove any old binaries or compilation artifacts. + +## Compiling the FAP +To compile the FAP, run `./fbt fap_rubiks_cube_scrambler`. + +## Launching the app +To run the Rubik's Cube Scrambler directly from the Flip.x0, use `./fbt launch_app APPSRC=rubiks_cube_scrambler`. + +# A special thanks to Tanish for their c scrambler example 🙏 +https://github.com/TanishBhongade/RubiksCubeScrambler-C/ diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam new file mode 100644 index 000000000..8d87a4a62 --- /dev/null +++ b/applications/plugins/scrambler/application.fam @@ -0,0 +1,20 @@ +# COMPILE ISTRUCTIONS: + +# Clean the code and remove old binaries/compilation artefact +# ./fbt -c fap_rubiks_cube_scrambler + +# Compile FAP +# ./fbt fap_rubiks_cube_scrambler + +# Run application directly inside the Flip.x0 +# ./fbt launch_app APPSRC=rubiks_cube_scrambler + +App( + appid="Rubiks_Cube_Scrambler", + name="Rubik's Cube Scrambler", + apptype=FlipperAppType.EXTERNAL, + entry_point="rubiks_cube_scrambler_main", + stack_size=1 * 1024, + fap_category="Misc", + fap_icon="cube.png", +) diff --git a/applications/plugins/scrambler/assets/1.png b/applications/plugins/scrambler/assets/1.png new file mode 100644 index 0000000000000000000000000000000000000000..d2099ea34bc3e6b6bb9718f9c15e6b412f7e94ef GIT binary patch literal 1964 zcmbuAe^AnA9LFDOp6Oj)S?yZg#2qc0%&qIrvJ@<*Y1+(l2}4D*G;3Wc5-1XC&8x*M z($s7qXy#P{YN#_sq+9FE>iCw5ga~f_=m!Xt2q*}SuC~7IcDuIz_XN%nKS&8LdxJ z!8mP1jR2M<0Mvc}wyr~fCD8yBd;gE=RhDK`SD_<>aa|4}yR}J1O486pijc44S_F?Z zw5AOTF=r#UNh_0jtRNu%+T*GLQ-sF1HTMiDdjk!TepY>~gaR?i48O77g8~4;JU~(0 z0^mE)iYZ#kFU$>%r|TtyOlsm#vbrFK9blpiCAW9Aw^yg&%?^T9RmUe8KAtbVo=8$J z>t+Y&+mjEaAIpbth_V2jxS>usbJ#3cm7m4F;@Ce|toym7w|ZajA=YK9Il%l>L*4H% zhTZEb>3V#ZIZGsc88E`H>hlB>i{6}uD6PdR1&}KKt7MWo9R*1aJWMiGw^`Ru*)$9{ zXH_6B#_67s|05{(eVqo^z4-wXO3L8udV1@JU}&zR({PYtAml|6Zj_NL%O;W z%h{;NJ*#hg!O0k;IsAgs3U)nWU0?8O>6db5`3=;98@v_oXZZWT@^1gy9;uIKx|ljD zaa!%BXO<+5YRXoLWlgu-){?5^vTEL;%Z?S+UV)|I9HdiIvq33VE@P}^v*XmlrAE-f4hJkruA z))inc!rb%ka0fSo(67+^lcCpj^AFPm=f#_7uAcBhV5y`gwsPvbZGzcS({L_k^&W-N z#&1%oPHC(u*K+1}XTB}beGz|q;T0xNC!;9?v3{x6=n7*@MS)>2ENl`nhpN@!OKF?G zsi0l!gNQHz$}vS=@C#>v?>bLF?#X{*zklx1`XT=+MZj3zUXwQ|w#1<_8ESLe8V{=D|{G zq5%8-gf>ykJ-ua$hKFLZ?D8iW5y29}T=%dNV#1E<@)H2=K3HP!*&p8x`TyK`HPqai zvJoSsCI-pu3drz9IC<9wcnK&M=Edje6V&rNd#!6AcpK=q03RJo5;$5pl;P^6jFrza z?@?0a)%yZ-gc?`V3+4F!&Q0gQK(no+nWCymI3X3U=25j%*=u3Y$tCW;S#P%#KWn?idpKqd-0`pJKq4K})Yp6QaqNTRKG<3A zsiiP48|f4se8IOhDd}brz9l&;6K8`sO+IA t&>(7LWYlC4adU~LN{0T4ZGxGm4Eb^njH~-D1pu`$c)I$ztaD0e0szja7u)~< literal 0 HcmV?d00001 diff --git a/applications/plugins/scrambler/rubiks_cube_scrambler.c b/applications/plugins/scrambler/rubiks_cube_scrambler.c new file mode 100644 index 000000000..4c845b883 --- /dev/null +++ b/applications/plugins/scrambler/rubiks_cube_scrambler.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include + +#include "scrambler.h" +#include "furi_hal_random.h" + +int scrambleStarted = 0; +char scramble_str[100] = {0}; +char scramble_start[100] = {0}; +char scramble_end[100] = {0}; +int notifications_enabled = 0; + +static void success_vibration() { + furi_hal_vibro_on(false); + furi_hal_vibro_on(true); + furi_delay_ms(50); + furi_hal_vibro_on(false); + return; +} +void split_array(char original[], int size, char first[], char second[]) { + int mid = size / 2; + if(size % 2 != 0) { + mid++; + } + int first_index = 0, second_index = 0; + for(int i = 0; i < size; i++) { + if(i < mid) { + first[first_index++] = original[i]; + } else { + if(i == mid && (original[i] == '2' || original[i] == '\'')) { + continue; + } + second[second_index++] = original[i]; + } + } + first[first_index] = '\0'; + second[second_index] = '\0'; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 13, "Rubik's Cube Scrambler"); + + if(scrambleStarted) { + genScramble(); + scrambleReplace(); + strcpy(scramble_str, printData()); + if(notifications_enabled) { + success_vibration(); + } + split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); + scrambleStarted = 0; + } + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, scramble_start); + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, scramble_end); + elements_button_center(canvas, "New"); + + elements_button_left(canvas, notifications_enabled ? "On" : "Off"); +}; + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t rubiks_cube_scrambler_main(void* p) { + UNUSED(p); + InputEvent event; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + + view_port_draw_callback_set(view_port, draw_callback, NULL); + + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + while(true) { + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + if(event.key == InputKeyOk && event.type == InputTypeShort) { + scrambleStarted = 1; + } + if(event.key == InputKeyLeft && event.type == InputTypeShort) { + if(notifications_enabled) { + notifications_enabled = 0; + } else { + notifications_enabled = 1; + success_vibration(); + } + } + if(event.key == InputKeyBack) { + break; + } + } + + furi_message_queue_free(event_queue); + + gui_remove_view_port(gui, view_port); + + view_port_free(view_port); + furi_record_close(RECORD_GUI); + return 0; +} diff --git a/applications/plugins/scrambler/scrambler.c b/applications/plugins/scrambler/scrambler.c new file mode 100644 index 000000000..ea5291940 --- /dev/null +++ b/applications/plugins/scrambler/scrambler.c @@ -0,0 +1,102 @@ +/* +Authors: Tanish Bhongade and RaZe +*/ + +#include +#include +#include +#include "furi_hal_random.h" +#include +#include +#include "scrambler.h" + +// 6 moves along with direction +char moves[6] = {'R', 'U', 'F', 'B', 'L', 'D'}; +char dir[4] = {' ', '\'', '2'}; +const int SLEN = 20; +#define RESULT_SIZE 100 +// Structure which holds main scramble +struct GetScramble { + char mainScramble[25][3]; +}; +struct GetScramble a; // Its object + +// Function prototypes to avoid bugs +void scrambleReplace(); +void genScramble(); +void valid(); +int getRand(int upr, int lwr); +char* printData(); +void writeToFile(); + +// Main function +/* int main(){ + genScramble ();//Calling genScramble + scrambleReplace();//Calling scrambleReplace + valid();//Calling valid to validate the scramble + printData ();//Printing the final scramble + //writeToFile();//If you want to write to a file, please uncomment this + + return 0; +} */ + +void genScramble() { + // Stage 1 + for(int i = 0; i < SLEN; i++) { + strcpy(a.mainScramble[i], "00"); + } + // This makes array like this 00 00 00....... +} + +void scrambleReplace() { + // Stage 2 + // Actual process begins here + + // Initialize the mainScramble array with all the possible moves + for(int i = 0; i < SLEN; i++) { + a.mainScramble[i][0] = moves[furi_hal_random_get() % 6]; + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + + // Perform the Fisher-Yates shuffle + for(int i = 6 - 1; i > 0; i--) { + int j = rand() % (i + 1); + char temp[3]; + strcpy(temp, a.mainScramble[i]); + strcpy(a.mainScramble[i], a.mainScramble[j]); + strcpy(a.mainScramble[j], temp); + } + + // Select the first 10 elements as the scramble, using only the first three elements of the dir array + for(int i = 0; i < SLEN; i++) { + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + for(int i = 1; i < SLEN; i++) { + while(a.mainScramble[i][0] == a.mainScramble[i - 2][0] || + a.mainScramble[i][0] == a.mainScramble[i - 1][0]) { + a.mainScramble[i][0] = moves[furi_hal_random_get() % 5]; + } + } +} + +// Let this function be here for now till I find out what is causing the extra space bug in the scrambles +void remove_double_spaces(char* str) { + int i, j; + int len = strlen(str); + for(i = 0, j = 0; i < len; i++, j++) { + if(str[i] == ' ' && str[i + 1] == ' ') { + i++; + } + str[j] = str[i]; + } + str[j] = '\0'; +} +char* printData() { + static char result[RESULT_SIZE]; + int offset = 0; + for(int loop = 0; loop < SLEN; loop++) { + offset += snprintf(result + offset, RESULT_SIZE - offset, "%s ", a.mainScramble[loop]); + } + remove_double_spaces(result); + return result; +} diff --git a/applications/plugins/scrambler/scrambler.h b/applications/plugins/scrambler/scrambler.h new file mode 100644 index 000000000..4b56c565d --- /dev/null +++ b/applications/plugins/scrambler/scrambler.h @@ -0,0 +1,3 @@ +void scrambleReplace(); +void genScramble(); +char* printData(); From cb2e8fbb52b699e7ea83a97eb8114c3770c2adc7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Wed, 8 Feb 2023 04:08:24 +0000 Subject: [PATCH 29/75] Add [GPIO] tag to geiger counter app --- applications/plugins/geigercounter/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam index 9fddb52b0..71be5a161 100644 --- a/applications/plugins/geigercounter/application.fam +++ b/applications/plugins/geigercounter/application.fam @@ -1,6 +1,6 @@ App( appid="Geiger_Coutner", - name="Geiger Counter", + name="[GPIO] Geiger Counter", apptype=FlipperAppType.EXTERNAL, entry_point="flipper_geiger_app", cdefines=["APP_GEIGER"], From e3d473bf429bd254b53550ebcbbb0d389b49240c Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 8 Feb 2023 07:41:22 +0300 Subject: [PATCH 30/75] [FL-2435] SD over SPI improvements (#2204) * get rid of BSP layer * sector_cache: init in any case * new sd-spi driver: init * Delete stm32_adafruit_sd.c.old * Delete spi_sd_hal.c.old * Storage: faster api lock primitive * Threads: priority control * Flags: correct error code * Spi: dma mode * SD-card: use DMA for big blocks of SPI data * Fix wrong SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE value * do not memset cache if it is NULL * remove top-level timeouts * sd hal: disable debug * Furi HAL: DMA * Furi HAL RFID: use furi_hal_dma * Furi HAL DMA: tests * Furi HAL DMA: docs * rollback "Furi HAL RFID: use furi_hal_dma" * 4 channels taken from DMA manager for core HAL. * Furi HAL DMA: live fast, die young * RPC tests: increase message buffer * SPI HAL: use second DMA instance * sd hal: new CID getter * SPI hal: use non-DMA version if kernel is not running * IR hal: generalize DMA definition * storage: add CID data to sd info * RFID hal: generalize DMA definition * SUBGHZ hal: generalize DMA definition. Core hal moved to DMA2. * Storage: small optimizations, removal of extra mutex * Storage: redundant macro * SD hal: more timeouts * SPI hal: DMA init * Target: make furi_hal_spi_dma_init symbol private * Update F18 api symbols Co-authored-by: Aleksandr Kutuzov --- applications/debug/unit_tests/rpc/rpc_test.c | 2 +- applications/services/storage/storage_cli.c | 21 +- .../services/storage/storage_external_api.c | 9 +- applications/services/storage/storage_glue.c | 18 +- applications/services/storage/storage_glue.h | 3 - .../services/storage/storage_message.h | 3 +- .../services/storage/storage_processing.c | 39 +- .../services/storage/storage_sd_api.h | 10 + .../services/storage/storages/storage_ext.c | 33 +- .../scenes/storage_settings_scene_sd_info.c | 21 +- firmware/targets/f18/api_symbols.csv | 6 +- firmware/targets/f18/furi_hal/furi_hal.c | 1 + firmware/targets/f7/api_symbols.csv | 6 +- firmware/targets/f7/fatfs/sd_spi_io.c | 877 +++++++++++++ firmware/targets/f7/fatfs/sd_spi_io.h | 157 +++ firmware/targets/f7/fatfs/sector_cache.c | 5 - firmware/targets/f7/fatfs/spi_sd_hal.c | 98 -- firmware/targets/f7/fatfs/stm32_adafruit_sd.c | 1113 ----------------- firmware/targets/f7/fatfs/stm32_adafruit_sd.h | 245 ---- firmware/targets/f7/fatfs/user_diskio.c | 36 +- firmware/targets/f7/fatfs/user_diskio.h | 2 +- firmware/targets/f7/furi_hal/furi_hal.c | 1 + .../targets/f7/furi_hal/furi_hal_infrared.c | 121 +- firmware/targets/f7/furi_hal/furi_hal_rfid.c | 46 +- firmware/targets/f7/furi_hal/furi_hal_spi.c | 231 +++- .../targets/f7/furi_hal/furi_hal_subghz.c | 49 +- firmware/targets/f7/src/update.c | 4 +- .../targets/furi_hal_include/furi_hal_spi.h | 20 + furi/core/event_flag.c | 2 +- furi/core/thread.c | 9 + furi/core/thread.h | 12 + 31 files changed, 1539 insertions(+), 1661 deletions(-) create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.c create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.h delete mode 100644 firmware/targets/f7/fatfs/spi_sd_hal.c delete mode 100644 firmware/targets/f7/fatfs/stm32_adafruit_sd.c delete mode 100644 firmware/targets/f7/fatfs/stm32_adafruit_sd.h diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 5b52df2fa..76acf6be9 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -89,7 +89,7 @@ static void test_rpc_setup(void) { } furi_check(rpc_session[0].session); - rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1); + rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index eeaa7fce8..ff2055697 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { SDInfo sd_info; - SD_CID sd_cid; FS_Error error = storage_sd_info(api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); if(error != FSE_OK) { storage_cli_print_error(error); } else { printf( "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" - "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", + "%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, sd_info.kb_free, - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); } } else { storage_cli_print_usage(); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 6929a9cbd..c5dfd533e 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -12,9 +12,7 @@ #define TAG "StorageAPI" -#define S_API_PROLOGUE \ - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \ - furi_check(semaphore != NULL); +#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked(); #define S_FILE_API_PROLOGUE \ Storage* storage = file->storage; \ @@ -24,13 +22,12 @@ furi_check( \ furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ FuriStatusOk); \ - furi_semaphore_acquire(semaphore, FuriWaitForever); \ - furi_semaphore_free(semaphore); + api_lock_wait_unlock_and_free(lock) #define S_API_MESSAGE(_command) \ SAReturn return_data; \ StorageMessage message = { \ - .semaphore = semaphore, \ + .lock = lock, \ .command = _command, \ .data = &data, \ .return_data = &return_data, \ diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c index 22f2e3dfa..cccf4046a 100644 --- a/applications/services/storage/storage_glue.c +++ b/applications/services/storage/storage_glue.c @@ -31,29 +31,13 @@ void storage_file_clear(StorageFile* obj) { /****************** storage data ******************/ void storage_data_init(StorageData* storage) { - storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(storage->mutex != NULL); storage->data = NULL; storage->status = StorageStatusNotReady; StorageFileList_init(storage->files); } -bool storage_data_lock(StorageData* storage) { - return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk); -} - -bool storage_data_unlock(StorageData* storage) { - return (furi_mutex_release(storage->mutex) == FuriStatusOk); -} - StorageStatus storage_data_status(StorageData* storage) { - StorageStatus status; - - storage_data_lock(storage); - status = storage->status; - storage_data_unlock(storage); - - return status; + return storage->status; } const char* storage_data_status_text(StorageData* storage) { diff --git a/applications/services/storage/storage_glue.h b/applications/services/storage/storage_glue.h index 6fdc70099..501c26abc 100644 --- a/applications/services/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -38,8 +38,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src); void storage_file_clear(StorageFile* obj); void storage_data_init(StorageData* storage); -bool storage_data_lock(StorageData* storage); -bool storage_data_unlock(StorageData* storage); StorageStatus storage_data_status(StorageData* storage); const char* storage_data_status_text(StorageData* storage); void storage_data_timestamp(StorageData* storage); @@ -57,7 +55,6 @@ struct StorageData { const FS_Api* fs_api; StorageApi api; void* data; - FuriMutex* mutex; StorageStatus status; StorageFileList_t files; uint32_t timestamp; diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 987268017..3edb1018e 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -1,5 +1,6 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -130,7 +131,7 @@ typedef enum { } StorageCommand; typedef struct { - FuriSemaphore* semaphore; + FuriApiLock lock; StorageCommand command; SAData* data; SAReturn* return_data; diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 795a5d11c..b1ea5d29b 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -2,15 +2,7 @@ #include #include -#define FS_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->fs_api->_fn; \ - storage_data_unlock(_storage); - -#define ST_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->api._fn; \ - storage_data_unlock(_storage); +#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { furi_check(type == ST_EXT || type == ST_INT); @@ -44,16 +36,11 @@ static const char* remove_vfs(const char* path) { static StorageType storage_get_type_by_path(Storage* app, const char* path) { StorageType type = ST_ERROR; - if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) && - memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { + if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { type = ST_EXT; - } else if( - strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) && - memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { + } else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { type = ST_INT; - } else if( - strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) && - memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { + } else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { type = ST_ANY; } @@ -68,21 +55,15 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) { } static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { - if(memcmp( - furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == - 0) { + if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { switch(real_storage) { case ST_EXT: - furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); break; case ST_INT: - furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX); break; default: break; @@ -604,7 +585,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { break; } - furi_semaphore_release(message->semaphore); + api_lock_unlock(message->lock); } void storage_process_message(Storage* app, StorageMessage* message) { diff --git a/applications/services/storage/storage_sd_api.h b/applications/services/storage/storage_sd_api.h index f83360955..880640394 100644 --- a/applications/services/storage/storage_sd_api.h +++ b/applications/services/storage/storage_sd_api.h @@ -23,6 +23,16 @@ typedef struct { uint16_t cluster_size; uint16_t sector_size; char label[SD_LABEL_LENGTH]; + + uint8_t manufacturer_id; + char oem_id[3]; + char product_name[6]; + uint8_t product_revision_major; + uint8_t product_revision_minor; + uint32_t product_serial_number; + uint8_t manufacturing_month; + uint16_t manufacturing_year; + FS_Error error; } SDInfo; diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 0c81a0006..530c88f85 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error); static bool sd_mount_card(StorageData* storage, bool notify) { bool result = false; - uint8_t counter = BSP_SD_MaxMountRetryCount(); + uint8_t counter = sd_max_mount_retry_count(); uint8_t bsp_result; SDData* sd_data = storage->data; - storage_data_lock(storage); - while(result == false && counter > 0 && hal_sd_detect()) { if(notify) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); @@ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { if((counter % 2) == 0) { // power reset sd card - bsp_result = BSP_SD_Init(true); + bsp_result = sd_init(true); } else { - bsp_result = BSP_SD_Init(false); + bsp_result = sd_init(false); } if(bsp_result) { @@ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } storage_data_timestamp(storage); - storage_data_unlock(storage); return result; } @@ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); storage->status = StorageStatusNotReady; error = FR_DISK_ERR; // TODO do i need to close the files? - f_mount(0, sd_data->path, 0); - storage_data_unlock(storage); + return storage_ext_parse_error(error); } @@ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); - work_area = malloc(_MAX_SS); error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS); free(work_area); @@ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) { storage->status = StorageStatusOK; } while(false); - storage_data_unlock(storage); - return storage_ext_parse_error(error); #endif } @@ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { memset(sd_info, 0, sizeof(SDInfo)); // get fs info - storage_data_lock(storage); error = f_getlabel(sd_data->path, sd_info->label, NULL); if(error == FR_OK) { #ifndef FURI_RAM_EXEC error = f_getfree(sd_data->path, &free_clusters, &fs); #endif } - storage_data_unlock(storage); if(error == FR_OK) { // calculate size @@ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { #endif } + SD_CID cid; + SdSpiStatus status = sd_get_cid(&cid); + + if(status == SdSpiStatusOK) { + sd_info->manufacturer_id = cid.ManufacturerID; + memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID)); + memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName)); + sd_info->product_revision_major = cid.ProdRev >> 4; + sd_info->product_revision_minor = cid.ProdRev & 0x0F; + sd_info->product_serial_number = cid.ProdSN; + sd_info->manufacturing_year = 2000 + cid.ManufactYear; + sd_info->manufacturing_month = cid.ManufactMonth; + } + return storage_ext_parse_error(error); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index a7991bc19..81c786d0c 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -1,5 +1,4 @@ #include "../storage_settings.h" -#include static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { StorageSettings* app = context; @@ -12,9 +11,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; SDInfo sd_info; - SD_CID sd_cid; FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); @@ -31,19 +28,19 @@ void storage_settings_scene_sd_info_on_enter(void* context) { furi_string_printf( app->text_string, "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" - "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, sd_info.kb_free, - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index e4a7dfdd5..ff6dbc239 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.1,, +Version,+,12.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1062,10 +1062,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* Function,+,furi_hal_uart_deinit,void,FuriHalUartId @@ -1231,6 +1233,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1244,6 +1247,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index ad35a0074..0a68fdb69 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -51,6 +51,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_speaker_init(); FURI_LOG_I(TAG, "Speaker OK"); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c1a979859..a1f34f106 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.1,, +Version,+,12.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1325,10 +1325,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1524,6 +1526,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1537,6 +1540,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c new file mode 100644 index 000000000..93b837e85 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -0,0 +1,877 @@ +#include "sd_spi_io.h" +#include "sector_cache.h" +#include +#include +#include + +// #define SD_SPI_DEBUG 1 +#define TAG "SdSpi" + +#ifdef SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH 6 +#define SD_DUMMY_BYTE 0xFF +#define SD_ANSWER_RETRY_COUNT 8 +#define SD_IDLE_RETRY_COUNT 100 +#define SD_BLOCK_SIZE 512 + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +static inline void sd_spi_select_card() { + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); + furi_delay_us(10); // Entry guard time for some SD cards +} + +static inline void sd_spi_deselect_card() { + furi_delay_us(10); // Exit guard time for some SD cards + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); +} + +static void sd_spi_bus_to_ground() { + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + + sd_spi_select_card(); + furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); +} + +static void sd_spi_bus_rise_up() { + sd_spi_deselect_card(); + + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); +} + +static inline uint8_t sd_spi_read_byte(void) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_byte(uint8_t data) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); +} + +static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static uint8_t sd_spi_wait_for_data_and_read(void) { + uint8_t retry_count = SD_ANSWER_RETRY_COUNT; + uint8_t responce; + + // Wait until we get a valid data + do { + responce = sd_spi_read_byte(); + retry_count--; + + } while((responce == SD_DUMMY_BYTE) && retry_count); + + return responce; +} + +static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); + uint8_t byte; + + do { + byte = sd_spi_read_byte(); + if(furi_hal_cortex_timer_is_expired(timer)) { + return SdSpiStatusTimeout; + } + } while((byte != data)); + + return SdSpiStatusOK; +} + +static inline void sd_spi_deselect_card_and_purge() { + sd_spi_deselect_card(); + sd_spi_read_byte(); +} + +static inline void sd_spi_purge_crc() { + sd_spi_read_byte(); + sd_spi_read_byte(); +} + +static SdSpiCmdAnswer + sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { + uint8_t frame[SD_CMD_LENGTH]; + SdSpiCmdAnswer cmd_answer = { + .r1 = SD_DUMMY_BYTE, + .r2 = SD_DUMMY_BYTE, + .r3 = SD_DUMMY_BYTE, + .r4 = SD_DUMMY_BYTE, + .r5 = SD_DUMMY_BYTE, + }; + + // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes + // R1b identical to R1 + Busy information + // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes + + frame[0] = ((uint8_t)cmd | 0x40); + frame[1] = (uint8_t)(arg >> 24); + frame[2] = (uint8_t)(arg >> 16); + frame[3] = (uint8_t)(arg >> 8); + frame[4] = (uint8_t)(arg); + frame[5] = (crc | 0x01); + + sd_spi_select_card(); + sd_spi_write_bytes(frame, sizeof(frame)); + + switch(answer_type) { + case SdSpiCmdAnswerTypeR1: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + break; + case SdSpiCmdAnswerTypeR1B: + // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + + // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B + // reassert card + sd_spi_deselect_card(); + furi_delay_us(1000); + sd_spi_deselect_card(); + + // and wait for it to be ready + while(sd_spi_read_byte() != 0xFF) { + }; + + break; + case SdSpiCmdAnswerTypeR2: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + break; + case SdSpiCmdAnswerTypeR3: + case SdSpiCmdAnswerTypeR7: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + cmd_answer.r3 = sd_spi_read_byte(); + cmd_answer.r4 = sd_spi_read_byte(); + cmd_answer.r5 = sd_spi_read_byte(); + break; + default: + break; + } + return cmd_answer; +} + +static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { + SdSpiDataResponce responce = sd_spi_read_byte(); + // read busy response byte + sd_spi_read_byte(); + + switch(responce & 0x1F) { + case SdSpiDataResponceOK: + // TODO: check timings + sd_spi_deselect_card(); + sd_spi_select_card(); + + // wait for 0xFF + if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static SdSpiStatus sd_spi_init_spi_mode_v1(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v1"); + + do { + retry_count++; + + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode_v2(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v2"); + + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + sd_spi_debug("ACMD41 is illegal command"); + retry_count = 0; + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { + sd_spi_debug("CMD55 failed"); + return SdSpiStatusError; + } + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode(void) { + SdSpiCmdAnswer response; + uint8_t retry_count; + + // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and + // wait for In Idle State Response (R1 Format) equal to 0x01 + retry_count = 0; + do { + retry_count++; + response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("CMD0 failed"); + return SdSpiStatusError; + } + } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); + + // CMD8 (SEND_IF_COND) to check the power supply status + // and wait until response (R7 Format) equal to 0xAA and + response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); + sd_spi_deselect_card_and_purge(); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) { + sd_spi_debug("Init mode v1 failed"); + return SdSpiStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) { + sd_spi_debug("Init mode v2 failed"); + return SdSpiStatusError; + } + + // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response + response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_debug("CMD58 failed"); + return SdSpiStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return SdSpiStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD9 (SEND_CSD): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CSD data + for(counter = 0; counter < 16; counter++) { + csd_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + /************************************************************************* + CSD header decoding + *************************************************************************/ + + csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; + csd->Reserved1 = csd_data[0] & 0x3F; + csd->TAAC = csd_data[1]; + csd->NSAC = csd_data[2]; + csd->MaxBusClkFrec = csd_data[3]; + csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); + csd->RdBlockLen = csd_data[5] & 0x0F; + csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; + csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; + csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; + csd->DSRImpl = (csd_data[6] & 0x10) >> 4; + + /************************************************************************* + CSD v1/v2 decoding + *************************************************************************/ + + if(sd_high_capacity == 0) { + csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); + csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | + ((csd_data[8] & 0xC0) >> 6); + csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; + csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); + csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; + csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; + csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | + ((csd_data[10] & 0x80) >> 7); + } else { + csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | + ((csd_data[7] & 0xC0) >> 6); + csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | + csd_data[9]; + csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); + } + + csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; + csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); + csd->WrProtectGrSize = (csd_data[11] & 0x7F); + csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; + csd->Reserved2 = (csd_data[12] & 0x60) >> 5; + csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; + csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); + csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; + csd->Reserved3 = (csd_data[13] & 0x1F); + csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; + csd->CopyFlag = (csd_data[14] & 0x40) >> 6; + csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; + csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; + csd->FileFormat = (csd_data[14] & 0x0C) >> 2; + csd->Reserved4 = (csd_data[14] & 0x03); + csd->crc = (csd_data[15] & 0xFE) >> 1; + csd->Reserved5 = (csd_data[15] & 0x01); + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD10 (SEND_CID): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CID data + for(counter = 0; counter < 16; counter++) { + cid_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + Cid->ManufacturerID = cid_data[0]; + memcpy(Cid->OEM_AppliID, cid_data + 1, 2); + memcpy(Cid->ProdName, cid_data + 3, 5); + Cid->ProdRev = cid_data[8]; + Cid->ProdSN = cid_data[9] << 24; + Cid->ProdSN |= cid_data[10] << 16; + Cid->ProdSN |= cid_data[11] << 8; + Cid->ProdSN |= cid_data[12]; + Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; + Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; + Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; + Cid->Reserved2 = 1; + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static SdSpiStatus + sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + SdSpiStatusOK) { + // Read the data block + sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } else { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_cmd_write_blocks( + uint32_t* data, + uint32_t address, + uint32_t blocks, + uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors) + response = sd_spi_send_cmd( + SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN + // TODO: check bytes count + sd_spi_write_byte(SD_DUMMY_BYTE); + sd_spi_write_byte(SD_DUMMY_BYTE); + + // Send the data start token + sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); + sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // Read data response + SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); + sd_spi_deselect_card_and_purge(); + + if(data_responce != SdSpiDataResponceOK) { + return SdSpiStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return SdSpiStatusOK; +} + +uint8_t sd_max_mount_retry_count() { + return 10; +} + +SdSpiStatus sd_init(bool power_reset) { + // Slow speed init + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; + + // We reset card in spi_lock context, so it is safe to disturb spi bus + if(power_reset) { + sd_spi_debug("Power reset"); + + // disable power and set low on all bus pins + furi_hal_power_disable_external_3_3v(); + sd_spi_bus_to_ground(); + hal_sd_detect_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + hal_sd_detect_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + SdSpiStatus status = SdSpiStatusError; + + // Send 80 dummy clocks with CS high + sd_spi_deselect_card(); + for(uint8_t i = 0; i < 80; i++) { + sd_spi_write_byte(SD_DUMMY_BYTE); + } + + for(uint8_t i = 0; i < 128; i++) { + status = sd_spi_init_spi_mode(); + if(status == SdSpiStatusOK) { + // SD initialized and init to SPI mode properly + sd_spi_debug("SD init OK after %d retries", i); + break; + } + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + + // Init sector cache + sector_cache_init(); + + return status; +} + +SdSpiStatus sd_get_card_state(void) { + SdSpiCmdAnswer response; + + // Send CMD13 (SEND_STATUS) to get SD status + response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); + sd_spi_deselect_card_and_purge(); + + // Return status OK if response is valid + if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { + return SdSpiStatusOK; + } + + return SdSpiStatusError; +} + +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { + SdSpiStatus status; + + status = sd_spi_get_csd(&(card_info->Csd)); + + if(status != SdSpiStatusOK) { + return status; + } + + status = sd_spi_get_cid(&(card_info->Cid)); + + if(status != SdSpiStatusOK) { + return status; + } + + if(sd_high_capacity == 1) { + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 512; + card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)card_info->LogBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } else { + card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1); + card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2)); + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen); + card_info->CardCapacity *= card_info->CardBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } + + return status; +} + +SdSpiStatus + sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + SdSpiStatus status = SdSpiStatusError; + + bool single_sector_read = (blocks == 1); + + if(single_sector_read) { + if(sd_cache_get(address, data)) { + return SdSpiStatusOK; + } + + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + + if(status == SdSpiStatusOK) { + sd_cache_put(address, data); + } + } else { + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + } + + return status; +} + +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + sd_cache_invalidate_range(address, address + blocks); + SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); + return status; +} + +SdSpiStatus sd_get_cid(SD_CID* cid) { + SdSpiStatus status; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + memset(cid, 0, sizeof(SD_CID)); + status = sd_spi_get_cid(cid); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h new file mode 100644 index 000000000..8850eceb7 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -0,0 +1,157 @@ +#pragma once +#include +#include + +#define __IO volatile + +#define SD_TIMEOUT_MS (1000) + +typedef enum { + SdSpiStatusOK, + SdSpiStatusError, + SdSpiStatusTimeout, +} SdSpiStatus; + +/** + * @brief Card Specific Data: CSD Register + */ +typedef struct { + /* Header part */ + uint8_t CSDStruct : 2; /* CSD structure */ + uint8_t Reserved1 : 6; /* Reserved */ + uint8_t TAAC : 8; /* Data read access-time 1 */ + uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ + uint16_t CardComdClasses : 12; /* Card command classes */ + uint8_t RdBlockLen : 4; /* Max. read data block length */ + uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ + uint8_t WrBlockMisalign : 1; /* Write block misalignment */ + uint8_t RdBlockMisalign : 1; /* Read block misalignment */ + uint8_t DSRImpl : 1; /* DSR implemented */ + + /* v1 or v2 struct */ + union csd_version { + struct { + uint8_t Reserved1 : 2; /* Reserved */ + uint16_t DeviceSize : 12; /* Device Size */ + uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ + uint8_t DeviceSizeMul : 3; /* Device size multiplier */ + } v1; + struct { + uint8_t Reserved1 : 6; /* Reserved */ + uint32_t DeviceSize : 22; /* Device Size */ + uint8_t Reserved2 : 1; /* Reserved */ + } v2; + } version; + + uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ + uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ + uint8_t WrProtectGrSize : 7; /* Write protect group size */ + uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ + uint8_t Reserved2 : 2; /* Reserved */ + uint8_t WrSpeedFact : 3; /* Write speed factor */ + uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ + uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ + uint8_t Reserved3 : 5; /* Reserved */ + uint8_t FileFormatGrouop : 1; /* File format group */ + uint8_t CopyFlag : 1; /* Copy flag (OTP) */ + uint8_t PermWrProtect : 1; /* Permanent write protection */ + uint8_t TempWrProtect : 1; /* Temporary write protection */ + uint8_t FileFormat : 2; /* File Format */ + uint8_t Reserved4 : 2; /* Reserved */ + uint8_t crc : 7; /* Reserved */ + uint8_t Reserved5 : 1; /* always 1*/ + +} SD_CSD; + +/** + * @brief Card Identification Data: CID Register + */ +typedef struct { + uint8_t ManufacturerID; /* ManufacturerID */ + char OEM_AppliID[2]; /* OEM/Application ID */ + char ProdName[5]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + uint32_t ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint8_t ManufactYear; /* Manufacturing Year */ + uint8_t ManufactMonth; /* Manufacturing Month */ + uint8_t CID_CRC; /* CID CRC */ + uint8_t Reserved2; /* always 1 */ +} SD_CID; + +/** + * @brief SD Card information structure + */ +typedef struct { + SD_CSD Csd; + SD_CID Cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ + uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ +} SD_CardInfo; + +/** + * @brief SD card max mount retry count + * + * @return uint8_t + */ +uint8_t sd_max_mount_retry_count(); + +/** + * @brief Init sd card + * + * @param power_reset reset card power + * @return SdSpiStatus + */ +SdSpiStatus sd_init(bool power_reset); + +/** + * @brief Get card state + * + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_state(void); + +/** + * @brief Get card info + * + * @param card_info + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info); + +/** + * @brief Read blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Write blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Get card CSD register + * + * @param Cid + * @return SdSpiStatus + */ +SdSpiStatus sd_get_cid(SD_CID* cid); \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sector_cache.c b/firmware/targets/f7/fatfs/sector_cache.c index d23c1d5ad..efb520ec8 100644 --- a/firmware/targets/f7/fatfs/sector_cache.c +++ b/firmware/targets/f7/fatfs/sector_cache.c @@ -8,7 +8,6 @@ #define SECTOR_SIZE 512 #define N_SECTORS 8 -#define TAG "SDCache" typedef struct { uint32_t itr; @@ -20,15 +19,11 @@ static SectorCache* cache = NULL; void sector_cache_init() { if(cache == NULL) { - // TODO: tuneup allocation order, to place cache in mem pool (MEM2) cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { - FURI_LOG_I(TAG, "Init"); memset(cache, 0, sizeof(SectorCache)); - } else { - FURI_LOG_E(TAG, "Init failed"); } } diff --git a/firmware/targets/f7/fatfs/spi_sd_hal.c b/firmware/targets/f7/fatfs/spi_sd_hal.c deleted file mode 100644 index bfe046b58..000000000 --- a/firmware/targets/f7/fatfs/spi_sd_hal.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#define SD_DUMMY_BYTE 0xFF - -const uint32_t SpiTimeout = 1000; -uint8_t SD_IO_WriteByte(uint8_t Data); - -/****************************************************************************** - BUS OPERATIONS - *******************************************************************************/ - -/** - * @brief SPI Write byte(s) to device - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -static void SPIx_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - furi_check(furi_hal_spi_bus_trx( - furi_hal_sd_spi_handle, (uint8_t*)DataIn, DataOut, DataLength, SpiTimeout)); -} - -/** - * @brief SPI Write a byte to device - * @param Value: value to be written - * @retval None - */ -__attribute__((unused)) static void SPIx_Write(uint8_t Value) { - furi_check(furi_hal_spi_bus_tx(furi_hal_sd_spi_handle, (uint8_t*)&Value, 1, SpiTimeout)); -} - -/****************************************************************************** - LINK OPERATIONS - *******************************************************************************/ - -/********************************* LINK SD ************************************/ -/** - * @brief Initialize the SD Card and put it into StandBy State (Ready for - * data transfer). - * @retval None - */ -void SD_IO_Init(void) { - uint8_t counter = 0; - - /* SD chip select high */ - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - furi_delay_us(10); - - /* Send dummy byte 0xFF, 10 times with CS high */ - /* Rise CS and MOSI for 80 clocks cycles */ - for(counter = 0; counter <= 200; counter++) { - /* Send dummy byte 0xFF */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - } -} - -/** - * @brief Set SD interface Chip Select state - * @param val: 0 (low) or 1 (high) state - * @retval None - */ -void SD_IO_CSState(uint8_t val) { - /* Some SD Cards are prone to fail if CLK-ed too soon after CS transition. Worst case found: 8us */ - if(val == 1) { - furi_delay_us(10); // Exit guard time for some SD cards - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - } else { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_delay_us(10); // Entry guard time for some SD cards - } -} - -/** - * @brief Write byte(s) on the SD - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - /* Send the byte */ - SPIx_WriteReadData(DataIn, DataOut, DataLength); -} - -/** - * @brief Write a byte on the SD. - * @param Data: byte to send. - * @retval Data written - */ -uint8_t SD_IO_WriteByte(uint8_t Data) { - uint8_t tmp; - - /* Send the byte */ - SPIx_WriteReadData(&Data, &tmp, 1); - return tmp; -} diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c deleted file mode 100644 index 998adee29..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ /dev/null @@ -1,1113 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.c - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file provides a set of functions needed to manage the SD card - * mounted on the Adafruit 1.8" TFT LCD shield (reference ID 802), - * that is used with the STM32 Nucleo board through SPI interface. - * It implements a high level communication layer for read and write - * from/to this memory. The needed STM32XXxx hardware resources (SPI and - * GPIO) are defined in stm32XXxx_nucleo.h file, and the initialization is - * performed in SD_IO_Init() function declared in stm32XXxx_nucleo.c - * file. - * You can easily tailor this driver to any other development board, - * by just adapting the defines for hardware resources and - * SD_IO_Init() function. - * - * +-------------------------------------------------------+ - * | Pin assignment | - * +-------------------------+---------------+-------------+ - * | STM32XXxx SPI Pins | SD | Pin | - * +-------------------------+---------------+-------------+ - * | SD_SPI_CS_PIN | ChipSelect | 1 | - * | SD_SPI_MOSI_PIN / MOSI | DataIn | 2 | - * | | GND | 3 (0 V) | - * | | VDD | 4 (3.3 V)| - * | SD_SPI_SCK_PIN / SCLK | Clock | 5 | - * | | GND | 6 (0 V) | - * | SD_SPI_MISO_PIN / MISO | DataOut | 7 | - * +-------------------------+---------------+-------------+ - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* File Info : ----------------------------------------------------------------- - User NOTES -1. How to use this driver: --------------------------- - - This driver does not need a specific component driver for the micro SD device - to be included with. - -2. Driver description: ---------------------- - + Initialization steps: - o Initialize the micro SD card using the BSP_SD_Init() function. - o Checking the SD card presence is not managed because SD detection pin is - not physically mapped on the Adafruit shield. - o The function BSP_SD_GetCardInfo() is used to get the micro SD card information - which is stored in the structure "SD_CardInfo". - - + Micro SD card operations - o The micro SD card can be accessed with read/write block(s) operations once - it is ready for access. The access can be performed in polling - mode by calling the functions BSP_SD_ReadBlocks()/BSP_SD_WriteBlocks() - - o The SD erase block(s) is performed using the function BSP_SD_Erase() with - specifying the number of blocks to erase. - o The SD runtime status is returned when calling the function BSP_SD_GetStatus(). - -------------------------------------------------------------------------------*/ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" -#include "stdlib.h" -#include "string.h" -#include "stdio.h" -#include -#include "sector_cache.h" - -/** @addtogroup BSP - * @{ - */ - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Types_Definitions - * @{ - */ -typedef struct { - uint8_t r1; - uint8_t r2; - uint8_t r3; - uint8_t r4; - uint8_t r5; -} SD_CmdAnswer_typedef; - -/** - * @} - */ - -/* Private define ------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Defines - * @{ - */ -#define SD_DUMMY_BYTE 0xFF - -#define SD_MAX_FRAME_LENGTH 17 /* Lenght = 16 + 1 */ -#define SD_CMD_LENGTH 6 - -#define SD_MAX_TRY 100 /* Number of try */ - -#define SD_CSD_STRUCT_V1 0x2 /* CSD struct version V1 */ -#define SD_CSD_STRUCT_V2 0x1 /* CSD struct version V2 */ - -/** - * @brief SD ansewer format - */ -typedef enum { - SD_ANSWER_R1_EXPECTED, - SD_ANSWER_R1B_EXPECTED, - SD_ANSWER_R2_EXPECTED, - SD_ANSWER_R3_EXPECTED, - SD_ANSWER_R4R5_EXPECTED, - SD_ANSWER_R7_EXPECTED, -} SD_Answer_type; - -/** - * @brief Start Data tokens: - * Tokens (necessary because at nop/idle (and CS active) only 0xff is - * on the data/command line) - */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Single Block Read */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Multiple Block Read */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE \ - 0xFE /* Data token start byte, Start Single Block Write */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data token start byte, Start Multiple Block Write */ -#define SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data toke stop byte, Stop Multiple Block Write */ - -/** - * @brief Commands: CMDxx = CMD-number | 0x40 - */ -#define SD_CMD_GO_IDLE_STATE 0 /* CMD0 = 0x40 */ -#define SD_CMD_SEND_OP_COND 1 /* CMD1 = 0x41 */ -#define SD_CMD_SEND_IF_COND 8 /* CMD8 = 0x48 */ -#define SD_CMD_SEND_CSD 9 /* CMD9 = 0x49 */ -#define SD_CMD_SEND_CID 10 /* CMD10 = 0x4A */ -#define SD_CMD_STOP_TRANSMISSION 12 /* CMD12 = 0x4C */ -#define SD_CMD_SEND_STATUS 13 /* CMD13 = 0x4D */ -#define SD_CMD_SET_BLOCKLEN 16 /* CMD16 = 0x50 */ -#define SD_CMD_READ_SINGLE_BLOCK 17 /* CMD17 = 0x51 */ -#define SD_CMD_READ_MULT_BLOCK 18 /* CMD18 = 0x52 */ -#define SD_CMD_SET_BLOCK_COUNT 23 /* CMD23 = 0x57 */ -#define SD_CMD_WRITE_SINGLE_BLOCK 24 /* CMD24 = 0x58 */ -#define SD_CMD_WRITE_MULT_BLOCK 25 /* CMD25 = 0x59 */ -#define SD_CMD_PROG_CSD 27 /* CMD27 = 0x5B */ -#define SD_CMD_SET_WRITE_PROT 28 /* CMD28 = 0x5C */ -#define SD_CMD_CLR_WRITE_PROT 29 /* CMD29 = 0x5D */ -#define SD_CMD_SEND_WRITE_PROT 30 /* CMD30 = 0x5E */ -#define SD_CMD_SD_ERASE_GRP_START 32 /* CMD32 = 0x60 */ -#define SD_CMD_SD_ERASE_GRP_END 33 /* CMD33 = 0x61 */ -#define SD_CMD_UNTAG_SECTOR 34 /* CMD34 = 0x62 */ -#define SD_CMD_ERASE_GRP_START 35 /* CMD35 = 0x63 */ -#define SD_CMD_ERASE_GRP_END 36 /* CMD36 = 0x64 */ -#define SD_CMD_UNTAG_ERASE_GROUP 37 /* CMD37 = 0x65 */ -#define SD_CMD_ERASE 38 /* CMD38 = 0x66 */ -#define SD_CMD_SD_APP_OP_COND 41 /* CMD41 = 0x69 */ -#define SD_CMD_APP_CMD 55 /* CMD55 = 0x77 */ -#define SD_CMD_READ_OCR 58 /* CMD55 = 0x79 */ - -/** - * @brief SD reponses and error flags - */ -typedef enum { - /* R1 answer value */ - SD_R1_NO_ERROR = (0x00), - SD_R1_IN_IDLE_STATE = (0x01), - SD_R1_ERASE_RESET = (0x02), - SD_R1_ILLEGAL_COMMAND = (0x04), - SD_R1_COM_CRC_ERROR = (0x08), - SD_R1_ERASE_SEQUENCE_ERROR = (0x10), - SD_R1_ADDRESS_ERROR = (0x20), - SD_R1_PARAMETER_ERROR = (0x40), - - /* R2 answer value */ - SD_R2_NO_ERROR = 0x00, - SD_R2_CARD_LOCKED = 0x01, - SD_R2_LOCKUNLOCK_ERROR = 0x02, - SD_R2_ERROR = 0x04, - SD_R2_CC_ERROR = 0x08, - SD_R2_CARD_ECC_FAILED = 0x10, - SD_R2_WP_VIOLATION = 0x20, - SD_R2_ERASE_PARAM = 0x40, - SD_R2_OUTOFRANGE = 0x80, - - /** - * @brief Data response error - */ - SD_DATA_OK = (0x05), - SD_DATA_CRC_ERROR = (0x0B), - SD_DATA_WRITE_ERROR = (0x0D), - SD_DATA_OTHER_ERROR = (0xFF) -} SD_Error; - -/** - * @} - */ - -/* Private macro -------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Macros - * @{ - */ - -/** - * @} - */ - -/* Private variables ---------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Variables - * @{ - */ -__IO uint8_t SdStatus = SD_NOT_PRESENT; - -/* flag_SDHC : - 0 : Standard capacity - 1 : High capacity -*/ -uint16_t flag_SDHC = 0; - -/** - * @} - */ - -/* Private function prototypes -----------------------------------------------*/ -static uint8_t SD_GetCIDRegister(SD_CID* Cid); -static uint8_t SD_GetCSDRegister(SD_CSD* Csd); -static uint8_t SD_GetDataResponse(void); -static uint8_t SD_GoIdleState(void); -static SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer); -static uint8_t SD_WaitData(uint8_t data); -static uint8_t SD_ReadData(void); -/** @defgroup STM32_ADAFRUIT_SD_Private_Function_Prototypes - * @{ - */ -/** - * @} - */ - -/* Private functions ---------------------------------------------------------*/ - -void SD_SPI_Bus_To_Down_State() { - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); -} - -void SD_SPI_Bus_To_Normal_State() { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); -} - -/** @defgroup STM32_ADAFRUIT_SD_Private_Functions - * @{ - */ - -uint8_t BSP_SD_MaxMountRetryCount() { - return 10; -} - -/** - * @brief Initializes the SD/SD communication. - * @param None - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_Init(bool reset_card) { - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - /* We must reset card in spi_lock context */ - if(reset_card) { - /* disable power and set low on all bus pins */ - furi_hal_power_disable_external_3_3v(); - SD_SPI_Bus_To_Down_State(); - hal_sd_detect_set_low(); - furi_delay_ms(250); - - /* reinit bus and enable power */ - SD_SPI_Bus_To_Normal_State(); - hal_sd_detect_init(); - furi_hal_power_enable_external_3_3v(); - furi_delay_ms(100); - } - - /* Configure IO functionalities for SD pin */ - SD_IO_Init(); - - /* SD detection pin is not physically mapped on the Adafruit shield */ - SdStatus = SD_PRESENT; - uint8_t res = BSP_SD_ERROR; - - for(uint8_t i = 0; i < 128; i++) { - res = SD_GoIdleState(); - if(res == BSP_SD_OK) break; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - - sector_cache_init(); - - /* SD initialized and set to SPI mode properly */ - return res; -} - -/** - * @brief Returns information about specific card. - * @param pCardInfo: Pointer to a SD_CardInfo structure that contains all SD - * card information. - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { - uint8_t status; - - status = SD_GetCSDRegister(&(pCardInfo->Csd)); - status |= SD_GetCIDRegister(&(pCardInfo->Cid)); - if(flag_SDHC == 1) { - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 512; - pCardInfo->CardCapacity = ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL * - (uint64_t)pCardInfo->LogBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } else { - pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); - pCardInfo->CardCapacity *= (1UL << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 1UL << (pCardInfo->Csd.RdBlockLen); - pCardInfo->CardCapacity *= pCardInfo->CardBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } - - return status; -} - -/** - * @brief Reads block(s) from a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param ReadAddr: Address from where data is to be read. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to read - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - uint8_t* cached_data; - - bool single_sector_read = (NumOfBlocks == 1); - if(single_sector_read && (cached_data = sector_cache_get(ReadAddr))) { - memcpy(pData, cached_data, BlockSize); - return BSP_SD_OK; - } - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (ReadAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */ - /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Now look for the data token to signify the start of the data */ - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Read the SD block data : read NumByteToRead data */ - SD_IO_WriteReadData(NULL, (uint8_t*)pData + offset, BlockSize); - - /* Set next read address*/ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } else { - goto error; - } - - /* End the command data read cycle */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - if(single_sector_read) { - sector_cache_put(ReadAddr, (uint8_t*)pData); - } - - retr = BSP_SD_OK; - -error: - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Writes block(s) to a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param WriteAddr: Address from where data is to be written. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to write - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t BSP_SD_WriteBlocks( - uint32_t* pData, - uint32_t WriteAddr, - uint32_t NumOfBlocks, - uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - sector_cache_invalidate_range(WriteAddr, WriteAddr + NumOfBlocks); - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (WriteAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write blocks and - Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Send dummy byte for NWR timing : one byte between CMDWRITE and TOKEN */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send the data token to signify the start of the data */ - SD_IO_WriteByte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); - - /* Write the block data to SD */ - SD_IO_WriteReadData((uint8_t*)pData + offset, NULL, BlockSize); - - /* Set next write address */ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* Put CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Read data response */ - if(SD_GetDataResponse() != SD_DATA_OK) { - /* Set response value to failure */ - goto error; - } - - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - retr = BSP_SD_OK; - -error: - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Erases the specified memory area of the given SD card. - * @param StartAddr: Start address in Blocks (Size of a block is 512bytes) - * @param EndAddr: End address in Blocks (Size of a block is 512bytes) - * @retval SD status - */ -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) { - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - - /* Send CMD32 (Erase group start) and check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_START, - (StartAddr) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD33 (Erase group end) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_END, - (EndAddr * 512) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD38 (Erase) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_ERASE, 0, 0xFF, SD_ANSWER_R1B_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - retr = BSP_SD_OK; - } - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - } - - /* Return the reponse */ - return retr; -} - -/** - * @brief Returns the SD status. - * @param None - * @retval The SD status. - */ -uint8_t BSP_SD_GetCardState(void) { - SD_CmdAnswer_typedef retr; - - /* Send CMD13 (SD_SEND_STATUS) to get SD status */ - retr = SD_SendCmd(SD_CMD_SEND_STATUS, 0, 0xFF, SD_ANSWER_R2_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Find SD status according to card state */ - if((retr.r1 == SD_R1_NO_ERROR) && (retr.r2 == SD_R2_NO_ERROR)) { - return BSP_SD_OK; - } - - return BSP_SD_ERROR; -} - -/** - * @brief Reads the SD card SCD register. - * Reading the contents of the CSD register in SPI mode is a simple - * read-block transaction. - * @param Csd: pointer on an SCD register structure - * @retval SD status - */ -uint8_t SD_GetCSDRegister(SD_CSD* Csd) { - uint16_t counter = 0; - uint8_t CSD_Tab[16]; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - - /* Send CMD9 (CSD register) or CMD10(CSD register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CSD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - for(counter = 0; counter < 16; counter++) { - /* Store CSD register value on CSD_Tab */ - CSD_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /************************************************************************* - CSD header decoding - *************************************************************************/ - - /* Byte 0 */ - Csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6; - Csd->Reserved1 = CSD_Tab[0] & 0x3F; - - /* Byte 1 */ - Csd->TAAC = CSD_Tab[1]; - - /* Byte 2 */ - Csd->NSAC = CSD_Tab[2]; - - /* Byte 3 */ - Csd->MaxBusClkFrec = CSD_Tab[3]; - - /* Byte 4/5 */ - Csd->CardComdClasses = (CSD_Tab[4] << 4) | ((CSD_Tab[5] & 0xF0) >> 4); - Csd->RdBlockLen = CSD_Tab[5] & 0x0F; - - /* Byte 6 */ - Csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7; - Csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6; - Csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5; - Csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4; - - /************************************************************************* - CSD v1/v2 decoding - *************************************************************************/ - - if(flag_SDHC == 0) { - Csd->version.v1.Reserved1 = ((CSD_Tab[6] & 0x0C) >> 2); - - Csd->version.v1.DeviceSize = ((CSD_Tab[6] & 0x03) << 10) | (CSD_Tab[7] << 2) | - ((CSD_Tab[8] & 0xC0) >> 6); - Csd->version.v1.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3; - Csd->version.v1.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07); - Csd->version.v1.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5; - Csd->version.v1.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2; - Csd->version.v1.DeviceSizeMul = ((CSD_Tab[9] & 0x03) << 1) | - ((CSD_Tab[10] & 0x80) >> 7); - } else { - Csd->version.v2.Reserved1 = ((CSD_Tab[6] & 0x0F) << 2) | - ((CSD_Tab[7] & 0xC0) >> 6); - Csd->version.v2.DeviceSize = ((CSD_Tab[7] & 0x3F) << 16) | (CSD_Tab[8] << 8) | - CSD_Tab[9]; - Csd->version.v2.Reserved2 = ((CSD_Tab[10] & 0x80) >> 8); - } - - Csd->EraseSingleBlockEnable = (CSD_Tab[10] & 0x40) >> 6; - Csd->EraseSectorSize = ((CSD_Tab[10] & 0x3F) << 1) | ((CSD_Tab[11] & 0x80) >> 7); - Csd->WrProtectGrSize = (CSD_Tab[11] & 0x7F); - Csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7; - Csd->Reserved2 = (CSD_Tab[12] & 0x60) >> 5; - Csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2; - Csd->MaxWrBlockLen = ((CSD_Tab[12] & 0x03) << 2) | ((CSD_Tab[13] & 0xC0) >> 6); - Csd->WriteBlockPartial = (CSD_Tab[13] & 0x20) >> 5; - Csd->Reserved3 = (CSD_Tab[13] & 0x1F); - Csd->FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7; - Csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6; - Csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5; - Csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4; - Csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2; - Csd->Reserved4 = (CSD_Tab[14] & 0x03); - Csd->crc = (CSD_Tab[15] & 0xFE) >> 1; - Csd->Reserved5 = (CSD_Tab[15] & 0x01); - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Reads the SD card CID register. - * Reading the contents of the CID register in SPI mode is a simple - * read-block transaction. - * @param Cid: pointer on an CID register structure - * @retval SD status - */ -uint8_t SD_GetCIDRegister(SD_CID* Cid) { - uint32_t counter = 0; - uint8_t retr = BSP_SD_ERROR; - uint8_t CID_Tab[16]; - SD_CmdAnswer_typedef response; - - /* Send CMD10 (CID register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CID, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Store CID register value on CID_Tab */ - for(counter = 0; counter < 16; counter++) { - CID_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Byte 0 */ - Cid->ManufacturerID = CID_Tab[0]; - - /* Byte 1 */ - memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); - - /* Byte 3 */ - memcpy(Cid->ProdName, CID_Tab + 3, 5); - - /* Byte 8 */ - Cid->ProdRev = CID_Tab[8]; - - /* Byte 9 */ - Cid->ProdSN = CID_Tab[9] << 24; - - /* Byte 10 */ - Cid->ProdSN |= CID_Tab[10] << 16; - - /* Byte 11 */ - Cid->ProdSN |= CID_Tab[11] << 8; - - /* Byte 12 */ - Cid->ProdSN |= CID_Tab[12]; - - /* Byte 13 */ - Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; - Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; - - /* Byte 14 */ - Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; - Cid->ManufactMonth = (CID_Tab[14] & 0x0F); - - /* Byte 15 */ - Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; - Cid->Reserved2 = 1; - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) { - uint8_t retr = BSP_SD_ERROR; - - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - memset(Cid, 0, sizeof(SD_CID)); - retr = SD_GetCIDRegister(Cid); - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - return retr; -} - -/** - * @brief Sends 5 bytes command to the SD card and get response - * @param Cmd: The user expected command to send to SD card. - * @param Arg: The command argument. - * @param Crc: The CRC. - * @param Answer: SD_ANSWER_NOT_EXPECTED or SD_ANSWER_EXPECTED - * @retval SD status - */ -SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer) { - uint8_t frame[SD_CMD_LENGTH], frameout[SD_CMD_LENGTH]; - SD_CmdAnswer_typedef retr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* R1 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes */ - /* R1b identical to R1 + Busy information */ - /* R2 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes */ - - /* Prepare Frame to send */ - frame[0] = (Cmd | 0x40); /* Construct byte 1 */ - frame[1] = (uint8_t)(Arg >> 24); /* Construct byte 2 */ - frame[2] = (uint8_t)(Arg >> 16); /* Construct byte 3 */ - frame[3] = (uint8_t)(Arg >> 8); /* Construct byte 4 */ - frame[4] = (uint8_t)(Arg); /* Construct byte 5 */ - frame[5] = (Crc | 0x01); /* Construct byte 6 */ - - /* Send the command */ - SD_IO_CSState(0); - SD_IO_WriteReadData(frame, frameout, SD_CMD_LENGTH); /* Send the Cmd bytes */ - - switch(Answer) { - case SD_ANSWER_R1_EXPECTED: - retr.r1 = SD_ReadData(); - break; - case SD_ANSWER_R1B_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - /* Set CS High */ - SD_IO_CSState(1); - furi_delay_us(1000); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_ANSWER_R2_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - case SD_ANSWER_R3_EXPECTED: - case SD_ANSWER_R7_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r3 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r4 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r5 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - default: - break; - } - return retr; -} - -/** - * @brief Gets the SD card data response and check the busy flag. - * @param None - * @retval The SD status: Read data response xxx01 - * - status 010: Data accecpted - * - status 101: Data rejected due to a crc error - * - status 110: Data rejected due to a Write error. - * - status 111: Data rejected due to other error. - */ -uint8_t SD_GetDataResponse(void) { - uint8_t dataresponse; - uint8_t rvalue = SD_DATA_OTHER_ERROR; - - dataresponse = SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); /* read the busy response byte*/ - - /* Mask unused bits */ - switch(dataresponse & 0x1F) { - case SD_DATA_OK: - rvalue = SD_DATA_OK; - - /* Set CS High */ - SD_IO_CSState(1); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_DATA_CRC_ERROR: - rvalue = SD_DATA_CRC_ERROR; - break; - case SD_DATA_WRITE_ERROR: - rvalue = SD_DATA_WRITE_ERROR; - break; - default: - break; - } - - /* Return response */ - return rvalue; -} - -/** - * @brief Put the SD in Idle state. - * @param None - * @retval SD status - */ -uint8_t SD_GoIdleState(void) { - SD_CmdAnswer_typedef response; - __IO uint8_t counter; - /* Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode and - wait for In Idle State Response (R1 Format) equal to 0x01 */ - counter = 0; - do { - counter++; - response = SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 != SD_R1_IN_IDLE_STATE); - - /* Send CMD8 (SD_CMD_SEND_IF_COND) to check the power supply status - and wait until response (R7 Format) equal to 0xAA and */ - response = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87, SD_ANSWER_R7_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - /* initialise card V1 */ - counter = 0; - do { - counter++; - /* initialise card V1 */ - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = //-V519 - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - flag_SDHC = 0; - } else if(response.r1 == SD_R1_IN_IDLE_STATE) { - /* initialise card V2 */ - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = //-V519 - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_IN_IDLE_STATE) { - return BSP_SD_ERROR; - } - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - } - - /* Send CMD58 (SD_CMD_READ_OCR) to initialize SDHC or SDXC cards: R3 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF, SD_ANSWER_R3_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - return BSP_SD_ERROR; - } - flag_SDHC = (response.r2 & 0x40) >> 6; - } else { - return BSP_SD_ERROR; - } - - return BSP_SD_OK; -} - -/** - * @brief Waits a data until a value different from SD_DUMMY_BITE - * @param None - * @retval the value read - */ -uint8_t SD_ReadData(void) { - uint8_t timeout = 0x08; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - - } while((readvalue == SD_DUMMY_BYTE) && timeout); - - /* Right response got */ - return readvalue; -} - -/** - * @brief Waits a data from the SD card - * @param data : Expected data from the SD card - * @retval BSP_SD_OK or BSP_SD_TIMEOUT - */ -uint8_t SD_WaitData(uint8_t data) { - uint16_t timeout = 0xFFFF; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - } while((readvalue != data) && timeout); - - if(timeout == 0) { - /* After time out */ - return BSP_SD_TIMEOUT; - } - - /* Right response got */ - return BSP_SD_OK; -} - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h deleted file mode 100644 index a133c5922..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ /dev/null @@ -1,245 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.h - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file contains the common defines and functions prototypes for - * the stm32_adafruit_sd.c driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32_ADAFRUIT_SD_H -#define __STM32_ADAFRUIT_SD_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include -#include - -/** @addtogroup BSP - * @{ - */ -#define __IO volatile - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Types - * @{ - */ - -/** - * @brief SD status structure definition - */ -enum { BSP_SD_OK = 0x00, MSD_OK = 0x00, BSP_SD_ERROR = 0x01, BSP_SD_TIMEOUT }; - -typedef struct { - uint8_t Reserved1 : 2; /* Reserved */ - uint16_t DeviceSize : 12; /* Device Size */ - uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ - uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ - uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ - uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ - uint8_t DeviceSizeMul : 3; /* Device size multiplier */ -} struct_v1; - -typedef struct { - uint8_t Reserved1 : 6; /* Reserved */ - uint32_t DeviceSize : 22; /* Device Size */ - uint8_t Reserved2 : 1; /* Reserved */ -} struct_v2; - -/** - * @brief Card Specific Data: CSD Register - */ -typedef struct { - /* Header part */ - uint8_t CSDStruct : 2; /* CSD structure */ - uint8_t Reserved1 : 6; /* Reserved */ - uint8_t TAAC : 8; /* Data read access-time 1 */ - uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ - uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ - uint16_t CardComdClasses : 12; /* Card command classes */ - uint8_t RdBlockLen : 4; /* Max. read data block length */ - uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ - uint8_t WrBlockMisalign : 1; /* Write block misalignment */ - uint8_t RdBlockMisalign : 1; /* Read block misalignment */ - uint8_t DSRImpl : 1; /* DSR implemented */ - - /* v1 or v2 struct */ - union csd_version { - struct_v1 v1; - struct_v2 v2; - } version; - - uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ - uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ - uint8_t WrProtectGrSize : 7; /* Write protect group size */ - uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ - uint8_t Reserved2 : 2; /* Reserved */ - uint8_t WrSpeedFact : 3; /* Write speed factor */ - uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ - uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ - uint8_t Reserved3 : 5; /* Reserved */ - uint8_t FileFormatGrouop : 1; /* File format group */ - uint8_t CopyFlag : 1; /* Copy flag (OTP) */ - uint8_t PermWrProtect : 1; /* Permanent write protection */ - uint8_t TempWrProtect : 1; /* Temporary write protection */ - uint8_t FileFormat : 2; /* File Format */ - uint8_t Reserved4 : 2; /* Reserved */ - uint8_t crc : 7; /* Reserved */ - uint8_t Reserved5 : 1; /* always 1*/ - -} SD_CSD; - -/** - * @brief Card Identification Data: CID Register - */ -typedef struct { - uint8_t ManufacturerID; /* ManufacturerID */ - char OEM_AppliID[2]; /* OEM/Application ID */ - char ProdName[5]; /* Product Name */ - uint8_t ProdRev; /* Product Revision */ - uint32_t ProdSN; /* Product Serial Number */ - uint8_t Reserved1; /* Reserved1 */ - uint8_t ManufactYear; /* Manufacturing Year */ - uint8_t ManufactMonth; /* Manufacturing Month */ - uint8_t CID_CRC; /* CID CRC */ - uint8_t Reserved2; /* always 1 */ -} SD_CID; - -/** - * @brief SD Card information - */ -typedef struct { - SD_CSD Csd; - SD_CID Cid; - uint64_t CardCapacity; /*!< Card Capacity */ - uint32_t CardBlockSize; /*!< Card Block Size */ - uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ - uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ -} SD_CardInfo; - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SPI_SD_Exported_Constants - * @{ - */ - -/** - * @brief Block Size - */ -#define SD_BLOCK_SIZE 0x200 - -/** - * @brief SD detection on its memory slot - */ -#define SD_PRESENT ((uint8_t)0x01) -#define SD_NOT_PRESENT ((uint8_t)0x00) - -#define SD_DATATIMEOUT ((uint32_t)100000000) - -/** - * @brief SD Card information structure - */ -#define BSP_SD_CardInfo SD_CardInfo - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Macro - * @{ - */ - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Functions - * @{ - */ -uint8_t BSP_SD_MaxMountRetryCount(); -uint8_t BSP_SD_Init(bool reset_card); -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t - BSP_SD_WriteBlocks(uint32_t* pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); -uint8_t BSP_SD_GetCardState(void); -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); - -/* Link functions for SD Card peripheral*/ -void SD_SPI_Slow_Init(void); -void SD_SPI_Fast_Init(void); -void SD_IO_Init(void); -void SD_IO_CSState(uint8_t state); -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength); -uint8_t SD_IO_WriteByte(uint8_t Data); - -/* Link function for HAL delay */ -void HAL_Delay(__IO uint32_t Delay); - -#ifdef __cplusplus -} -#endif - -#endif /* __STM32_ADAFRUIT_SD_H */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index b504fcd51..16ac78e4d 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -46,7 +46,7 @@ static volatile DSTATUS Stat = STA_NOINIT; static DSTATUS User_CheckStatus(BYTE lun) { UNUSED(lun); Stat = STA_NOINIT; - if(BSP_SD_GetCardState() == MSD_OK) { + if(sd_get_card_state() == SdSpiStatusOK) { Stat &= ~STA_NOINIT; } @@ -128,11 +128,18 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the read operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; @@ -160,11 +167,18 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the Write operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; @@ -187,7 +201,7 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* USER CODE BEGIN IOCTL */ UNUSED(pdrv); DRESULT res = RES_ERROR; - BSP_SD_CardInfo CardInfo; + SD_CardInfo CardInfo; if(Stat & STA_NOINIT) return RES_NOTRDY; @@ -202,21 +216,21 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* Get number of sectors on the disk (DWORD) */ case GET_SECTOR_COUNT: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockNbr; res = RES_OK; break; /* Get R/W sector size (WORD) */ case GET_SECTOR_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(WORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; /* Get erase block size in unit of sector (DWORD) */ case GET_BLOCK_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; diff --git a/firmware/targets/f7/fatfs/user_diskio.h b/firmware/targets/f7/fatfs/user_diskio.h index 177723be1..12e0f27dc 100644 --- a/firmware/targets/f7/fatfs/user_diskio.h +++ b/firmware/targets/f7/fatfs/user_diskio.h @@ -30,7 +30,7 @@ extern "C" { /* USER CODE BEGIN 0 */ /* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" +#include "sd_spi_io.h" #include "fatfs/ff_gen_drv.h" /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 7f2f5759f..afe46c4ed 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -53,6 +53,7 @@ void furi_hal_init() { furi_hal_region_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_ibutton_init(); FURI_LOG_I(TAG, "iButton OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index dea1112ab..c1d24f803 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -28,6 +28,15 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GPIO_PIN_7}; #define INFRARED_TX_CCMR_LOW \ (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ +/* DMA Channels definition */ +#define IR_DMA DMA2 +#define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 +#define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL +#define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL + typedef struct { FuriHalInfraredRxCaptureCallback capture_callback; void* capture_context; @@ -213,15 +222,15 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback( } static void furi_hal_infrared_tx_dma_terminate(void) { - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF); furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); LL_TIM_DisableCounter(TIM1); FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); furi_check(status == FuriStatusOk); @@ -230,7 +239,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) { static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { uint8_t buf_num = 0; - uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2); + uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF); if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { buf_num = 0; } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { @@ -242,12 +251,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { } static void furi_hal_infrared_tx_dma_polarity_isr() { - if(LL_DMA_IsActiveFlag_TE1(DMA1)) { - LL_DMA_ClearFlag_TE1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) { + LL_DMA_ClearFlag_TE1(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) { + LL_DMA_ClearFlag_TC1(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTx) || @@ -257,25 +267,29 @@ static void furi_hal_infrared_tx_dma_polarity_isr() { uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_tx_dma_isr() { - if(LL_DMA_IsActiveFlag_TE2(DMA1)) { - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) { + LL_DMA_ClearFlag_TE2(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_HT2(DMA1); + if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_HT2(IR_DMA); uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); uint8_t next_buf_num = !buf_num; if(infrared_tim_tx.buffer[buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } else if( !infrared_tim_tx.buffer[buf_num].packet_end || (furi_hal_infrared_state == InfraredStateAsyncTx)) { furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { /* fallthrough */ @@ -283,8 +297,8 @@ static void furi_hal_infrared_tx_dma_isr() { furi_crash(NULL); } } - if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_TC2(DMA1); + if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_TC2(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || @@ -310,6 +324,9 @@ static void furi_hal_infrared_tx_dma_isr() { infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); } } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { @@ -369,16 +386,19 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config); - LL_DMA_ClearFlag_TE1(DMA1); - LL_DMA_ClearFlag_TC1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + LL_DMA_ClearFlag_TE1(IR_DMA); + LL_DMA_ClearFlag_TC1(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF); + LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -394,18 +414,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config); - LL_DMA_ClearFlag_TC2(DMA1); - LL_DMA_ClearFlag_HT2(DMA1); - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + LL_DMA_ClearFlag_TC2(IR_DMA); + LL_DMA_ClearFlag_HT2(IR_DMA); + LL_DMA_ClearFlag_TE2(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { @@ -507,14 +530,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar furi_assert(buffer->polarity != NULL); FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); + LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity); + LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); } FURI_CRITICAL_EXIT(); } @@ -527,14 +550,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { /* non-circular mode requires disabled channel before setup */ FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); + LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data); + LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); } @@ -545,8 +568,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch2, NULL, NULL); + furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); + furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); LL_TIM_DeInit(TIM1); furi_semaphore_free(infrared_tim_tx.stop_semaphore); @@ -597,8 +620,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_hal_infrared_state = InfraredStateAsyncTx; LL_TIM_ClearFlag_UPDATE(TIM1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); furi_delay_us(5); LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ furi_delay_us(5); diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 0ade85e0a..b0238f79f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -21,6 +21,14 @@ #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +/* DMA Channels definition */ +#define RFID_DMA DMA2 +#define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define RFID_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define RFID_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL +#define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL + typedef struct { FuriHalRfidEmulateCallback callback; FuriHalRfidDMACallback dma_callback; @@ -302,15 +310,19 @@ void furi_hal_rfid_tim_read_capture_stop() { } static void furi_hal_rfid_dma_isr() { - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); +#if RFID_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(RFID_DMA)) { + LL_DMA_ClearFlag_HT1(RFID_DMA); furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(RFID_DMA)) { + LL_DMA_ClearFlag_TC1(RFID_DMA); furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); } +#else +#error Update this code. Would you kindly? +#endif } void furi_hal_rfid_tim_emulate_dma_start( @@ -347,8 +359,8 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(RFID_DMA_CH1_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); // configure DMA "mem -> CCR3" channel #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 @@ -366,13 +378,13 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(RFID_DMA_CH2_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, furi_hal_rfid_dma_isr, NULL); + LL_DMA_EnableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(RFID_DMA_CH1_DEF); // start LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); @@ -385,14 +397,14 @@ void furi_hal_rfid_tim_emulate_dma_stop() { LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, NULL, NULL); + LL_DMA_DisableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(RFID_DMA_CH1_DEF); FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); FURI_CRITICAL_EXIT(); @@ -471,4 +483,4 @@ void COMP_IRQHandler() { (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), furi_hal_rfid_comp_callback_context); } -} +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2f9d87080..8dba8327f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,14 +1,33 @@ +#include #include #include #include +#include -#include -#include - +#include #include #include #include +#define TAG "FuriHalSpi" + +#define SPI_DMA DMA2 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL +#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL + +// For simplicity, I assume that only one SPI DMA transaction can occur at a time. +static FuriSemaphore* spi_dma_lock = NULL; +static FuriSemaphore* spi_dma_completed = NULL; + +void furi_hal_spi_dma_init() { + spi_dma_lock = furi_semaphore_alloc(1, 1); + spi_dma_completed = furi_semaphore_alloc(1, 1); +} + void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); @@ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx( return ret; } + +static void spi_dma_isr() { +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC3(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC4(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif +} + +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + // If scheduler is not running, use blocking mode + if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); + } + + // Lock DMA + furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); + + const uint32_t dma_dummy_u32 = 0xFFFFFFFF; + + bool ret = true; + SPI_TypeDef* spi = handle->bus->spi; + uint32_t dma_rx_req; + uint32_t dma_tx_req; + + if(spi == SPI1) { + dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; + } else if(spi == SPI2) { + dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; + } else { + furi_crash(NULL); + } + + if(rx_buffer == NULL) { + // Only TX mode, do not use RX channel + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + LL_DMA_ClearFlag_TC4(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + } else { + // TRX or RX mode, use both channels + uint32_t tx_mem_increase_mode; + + if(tx_buffer == NULL) { + // RX mode, use dummy data instead of TX buffer + tx_buffer = (uint8_t*)&dma_dummy_u32; + tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; + } else { + tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; + } + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + LL_DMA_ClearFlag_TC3(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); + + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); + + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); + + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); + } + + furi_hal_spi_bus_end_txrx(handle, timeout_ms); + + furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); + + return ret; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 9bde15c33..a6d275308 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -18,6 +18,14 @@ static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +/* DMA Channels definition */ +#define SUBGHZ_DMA DMA2 +#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL +#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL + typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; @@ -525,8 +533,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { *buffer = 0; buffer++; samples--; - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); LL_TIM_EnableIT_UPDATE(TIM2); break; } else { @@ -567,17 +575,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { static void furi_hal_subghz_async_tx_dma_isr() { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); + +#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_subghz_async_tx_timer_isr() { @@ -586,7 +599,7 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; //forcibly pulls the pin to the ground so that there is no carrier @@ -634,11 +647,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); + LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); // Configure TIM2 LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -696,9 +709,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); + LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } return true; @@ -726,16 +739,16 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); // Deinitialize DMA - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); // Deinitialize GPIO furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // Stop debug if(furi_hal_subghz_stop_debug()) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index b223a5dc3..d8d26eb7c 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -23,8 +23,8 @@ static FATFS* pfs = NULL; } static bool flipper_update_mount_sd() { - for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { - if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { + for(int i = 0; i < sd_max_mount_retry_count(); ++i) { + if(sd_init((i % 2) == 0) != SdSpiStatusOK) { /* Next attempt will be without card reset, let it settle */ furi_delay_ms(1000); continue; diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index ab00ef0d7..79e809317 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ void furi_hal_spi_config_init(); +/** Initialize SPI DMA HAL */ +void furi_hal_spi_dma_init(); + /** Initialize SPI Bus * * @param handle pointer to FuriHalSpiBus instance @@ -103,6 +106,23 @@ bool furi_hal_spi_bus_trx( size_t size, uint32_t timeout); +/** SPI Transmit and Receive with DMA + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param tx_buffer pointer to tx buffer + * @param rx_buffer pointer to rx buffer + * @param size transaction size (buffer size) + * @param timeout_ms operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms); + #ifdef __cplusplus } #endif diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 87de65f2d..9406a581f 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -32,7 +32,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { - rflags = (uint32_t)FuriStatusErrorResource; + rflags = (uint32_t)FuriFlagErrorResource; } else { rflags = flags; portYIELD_FROM_ISR(yield); diff --git a/furi/core/thread.c b/furi/core/thread.c index ef9560b4a..9a112d9a8 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -195,6 +195,15 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { thread->priority = priority; } +void furi_thread_set_current_priority(FuriThreadPriority priority) { + UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal; + vTaskPrioritySet(NULL, new_priority); +} + +FuriThreadPriority furi_thread_get_current_priority() { + return (FuriThreadPriority)uxTaskPriorityGet(NULL); +} + void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); diff --git a/furi/core/thread.h b/furi/core/thread.h index 1542d5bf0..2675f7cee 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -122,6 +122,18 @@ void furi_thread_set_context(FuriThread* thread, void* context); */ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); +/** Set current thread priority + * + * @param priority FuriThreadPriority value + */ +void furi_thread_set_current_priority(FuriThreadPriority priority); + +/** Get current thread priority + * + * @return FuriThreadPriority value + */ +FuriThreadPriority furi_thread_get_current_priority(); + /** Set FuriThread state change callback * * @param thread FuriThread instance From 7a3a1aaf0db35501b6ee5891ad039605285f2fea Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:40:44 +0300 Subject: [PATCH 31/75] [FL-3057] Allow use of any suitable pin for 1-Wire devices (#2350) * Add 1-wire thermometer example app stub * Working 1-wire thermometer app * Refactor app to use threads * Clean up code, add comments * Add CRC checking * Increase update period * Fix error in fbt * Revert the old update period * Use settable pin in onewire_host * Use settable pin for onewire_slave * Clear EXTI flag after callback, make private methods static in onewire_slave * Do not hardcode GPIO pin number * Remove iButton hal from furi_hal_rfid * Remove most of furi_hal_ibutton * Add some of furi_hal_ibutton back * Slightly neater code * Fix formatting * Fix PVS-studio warnings * Update CODEOWNERS * Add furi_hal_gpio_get_ext_pin_number * Create README.md * FuriHal: move furi_hal_gpio_get_ext_pin_number to resources --------- Co-authored-by: Aleksandr Kutuzov --- .github/CODEOWNERS | 2 + .../examples/example_thermo/README.md | 44 +++ .../examples/example_thermo/application.fam | 10 + .../examples/example_thermo/example_thermo.c | 356 ++++++++++++++++++ .../example_thermo/example_thermo_10px.png | Bin 0 -> 7293 bytes applications/main/ibutton/ibutton_cli.c | 2 +- firmware/targets/f18/api_symbols.csv | 3 +- .../targets/f18/furi_hal/furi_hal_resources.c | 24 ++ .../targets/f18/furi_hal/furi_hal_resources.h | 7 + firmware/targets/f7/api_symbols.csv | 20 +- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 33 +- .../targets/f7/furi_hal/furi_hal_ibutton.c | 43 +-- .../targets/f7/furi_hal/furi_hal_ibutton.h | 66 +--- .../targets/f7/furi_hal/furi_hal_resources.c | 23 ++ .../targets/f7/furi_hal/furi_hal_resources.h | 7 + firmware/targets/f7/furi_hal/furi_hal_rfid.c | 10 +- lib/one_wire/ibutton/ibutton_worker.c | 4 +- lib/one_wire/ibutton/ibutton_worker_modes.c | 13 +- lib/one_wire/one_wire_host.c | 48 ++- lib/one_wire/one_wire_host.h | 6 +- lib/one_wire/one_wire_slave.c | 51 +-- lib/one_wire/one_wire_slave.h | 4 +- 22 files changed, 592 insertions(+), 184 deletions(-) create mode 100644 applications/examples/example_thermo/README.md create mode 100644 applications/examples/example_thermo/application.fam create mode 100644 applications/examples/example_thermo/example_thermo.c create mode 100644 applications/examples/example_thermo/example_thermo_10px.png diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aa4ada1a8..69f8289f9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -42,6 +42,8 @@ /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm +/applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov + # Assets /assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md new file mode 100644 index 000000000..fa00264dc --- /dev/null +++ b/applications/examples/example_thermo/README.md @@ -0,0 +1,44 @@ +# 1-Wire Thermometer +This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. +It also covers basic GUI, input handling, threads and localisation. + +## Electrical connections +Before launching the application, connect the sensor to Flipper's external GPIO according to the table below: +| DS18B20 | Flipper | +| :-----: | :-----: | +| VDD | 9 | +| GND | 18 | +| DQ | 17 | + +*NOTE 1*: GND is also available on pins 8 and 11. + +*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9. + +## Launching the application +In order to launch this demo, follow the steps below: +1. Make sure your Flipper has an SD card installed. +2. Connect your Flipper to the computer via a USB cable. +3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice. + +## Changing the data pin +It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: + +```c +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) +``` +Do not forget about the external pull-up resistor as these pins do not have one built-in. + +With the changes been made, recompile and launch the application again. +The on-screen text should reflect it by asking to connect the thermometer to another pin. diff --git a/applications/examples/example_thermo/application.fam b/applications/examples/example_thermo/application.fam new file mode 100644 index 000000000..b4a05c7f9 --- /dev/null +++ b/applications/examples/example_thermo/application.fam @@ -0,0 +1,10 @@ +App( + appid="example_thermo", + name="Example: Thermometer", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_thermo_main", + requires=["gui"], + stack_size=1 * 1024, + fap_icon="example_thermo_10px.png", + fap_category="Examples", +) diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c new file mode 100644 index 000000000..b3bc7cd99 --- /dev/null +++ b/applications/examples/example_thermo/example_thermo.c @@ -0,0 +1,356 @@ +/* + * This file contains an example application that reads and displays + * the temperature from a DS18B20 1-wire thermometer. + * + * It also covers basic GUI, input handling, threads and localisation. + * + * References: + * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf + */ + +#include +#include + +#include +#include + +#include + +#include +#include + +#define UPDATE_PERIOD_MS 1000UL +#define TEXT_STORE_SIZE 64U + +#define DS18B20_CMD_CONVERT 0x44U +#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU + +#define DS18B20_CFG_RESOLUTION_POS 5U +#define DS18B20_CFG_RESOLUTION_MASK 0x03U +#define DS18B20_DECIMAL_PART_MASK 0x0fU + +#define DS18B20_SIGN_MASK 0xf0U + +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) + +/* Flags which the reader thread responds to */ +typedef enum { + ReaderThreadFlagExit = 1, +} ReaderThreadFlag; + +typedef union { + struct { + uint8_t temp_lsb; /* Least significant byte of the temperature */ + uint8_t temp_msb; /* Most significant byte of the temperature */ + uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ + uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ + uint8_t config; /* Configuration register */ + uint8_t reserved[3]; /* Not used */ + uint8_t crc; /* CRC checksum for error detection */ + } fields; + uint8_t bytes[9]; +} DS18B20Scratchpad; + +/* Application context structure */ +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriThread* reader_thread; + FuriMessageQueue* event_queue; + OneWireHost* onewire; + float temp_celsius; + bool has_device; +} ExampleThermoContext; + +/*************** 1-Wire Communication and Processing *****************/ + +/* Commands the thermometer to begin measuring the temperature. */ +static void example_thermo_request_temperature(ExampleThermoContext* context) { + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) break; + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + /* After the ROM operation, a device-specific command is issued. + In this case, it's a request to start measuring the temperature. */ + onewire_host_write(onewire, DS18B20_CMD_CONVERT); + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Reads the measured temperature from the thermometer. */ +static void example_thermo_read_temperature(ExampleThermoContext* context) { + /* If there was no device detected, don't try to read the temperature */ + if(!context->has_device) { + return; + } + + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + + do { + DS18B20Scratchpad buf; + + /* Attempt reading the temperature 10 times before giving up */ + size_t attempts_left = 10; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) continue; + + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + + /* After the ROM operation, a device-specific command is issued. + This time, it will be the "Read Scratchpad" command which will + prepare the device's internal buffer memory for reading. */ + onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); + + /* The actual reading happens here. A total of 9 bytes is read. */ + onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); + + /* Calculate the checksum and compare it with one provided by the device. */ + const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); + + /* Checksums match, exit the loop */ + if(crc == buf.fields.crc) break; + + } while(--attempts_left); + + if(attempts_left == 0) break; + + /* Get the measurement resolution from the configuration register. (See [1] page 9) */ + const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & + DS18B20_CFG_RESOLUTION_MASK; + + /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ + const uint8_t decimal_mask = + (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & + DS18B20_DECIMAL_PART_MASK; + + /* Get the integer and decimal part of the temperature (See [1] page 6) */ + const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); + const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; + + /* Calculate the sign of the temperature (See [1] page 6) */ + const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; + + /* Combine the integer and decimal part together */ + const float temp_celsius_abs = integer_part + decimal_part / 16.f; + + /* Set the appropriate sign */ + context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ +static int32_t example_thermo_reader_thread_callback(void* ctx) { + ExampleThermoContext* context = ctx; + + for(;;) { + /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ + example_thermo_request_temperature(context); + + /* Wait for the measurement to finish. At the same time wait for an exit signal. */ + const uint32_t flags = + furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); + + /* If an exit signal was received, return from this thread. */ + if(flags != (unsigned)FuriFlagErrorTimeout) break; + + /* The measurement is now ready, read it from the termometer. */ + example_thermo_read_temperature(context); + } + + return 0; +} + +/*************** GUI, Input and Main Loop *****************/ + +/* Draw the GUI of the application. The screen is completely redrawn during each call. */ +static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { + ExampleThermoContext* context = ctx; + char text_store[TEXT_STORE_SIZE]; + const size_t middle_x = canvas_width(canvas) / 2U; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); + canvas_draw_line(canvas, 0, 16, 128, 16); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer"); + + snprintf( + text_store, + TEXT_STORE_SIZE, + "to GPIO pin %ld", + furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); + canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); + + canvas_set_font(canvas, FontKeyboard); + + if(context->has_device) { + float temp; + char temp_units; + + /* The applicaton is locale-aware. + Change Settings->System->Units to check it out. */ + switch(locale_get_measurement_unit()) { + case LocaleMeasurementUnitsMetric: + temp = context->temp_celsius; + temp_units = 'C'; + break; + case LocaleMeasurementUnitsImperial: + temp = locale_celsius_to_fahrenheit(context->temp_celsius); + temp_units = 'F'; + break; + default: + furi_crash("Illegal measurement units"); + } + /* If a reading is available, display it */ + snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); + } else { + /* Or show a message that no data is available */ + strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + } + + canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); +} + +/* This function is called from the GUI thread. All it does is put the event + into the application's queue so it can be processed later. */ +static void example_thermo_input_callback(InputEvent* event, void* ctx) { + ExampleThermoContext* context = ctx; + furi_message_queue_put(context->event_queue, event, FuriWaitForever); +} + +/* Starts the reader thread and handles the input */ +static void example_thermo_run(ExampleThermoContext* context) { + /* Configure the hardware in host mode */ + onewire_host_start(context->onewire); + + /* Start the reader thread. It will talk to the thermometer in the background. */ + furi_thread_start(context->reader_thread); + + /* An endless loop which handles the input*/ + for(bool is_running = true; is_running;) { + InputEvent event; + /* Wait for an input event. Input events come from the GUI thread via a callback. */ + const FuriStatus status = + furi_message_queue_get(context->event_queue, &event, FuriWaitForever); + + /* This application is only interested in short button presses. */ + if((status != FuriStatusOk) || (event.type != InputTypeShort)) { + continue; + } + + /* When the user presses the "Back" button, break the loop and exit the application. */ + if(event.key == InputKeyBack) { + is_running = false; + } + } + + /* Signal the reader thread to cease operation and exit */ + furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); + + /* Wait for the reader thread to finish */ + furi_thread_join(context->reader_thread); + + /* Reset the hardware */ + onewire_host_stop(context->onewire); +} + +/******************** Initialisation & startup *****************************/ + +/* Allocate the memory and initialise the variables */ +static ExampleThermoContext* example_thermo_context_alloc() { + ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); + + context->view_port = view_port_alloc(); + view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); + view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); + + context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + context->reader_thread = furi_thread_alloc(); + furi_thread_set_stack_size(context->reader_thread, 1024U); + furi_thread_set_context(context->reader_thread, context); + furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); + + context->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); + + context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); + + return context; +} + +/* Release the unused resources and deallocate memory */ +static void example_thermo_context_free(ExampleThermoContext* context) { + view_port_enabled_set(context->view_port, false); + gui_remove_view_port(context->gui, context->view_port); + + onewire_host_free(context->onewire); + furi_thread_free(context->reader_thread); + furi_message_queue_free(context->event_queue); + view_port_free(context->view_port); + + furi_record_close(RECORD_GUI); +} + +/* The application's entry point. Execution starts from here. */ +int32_t example_thermo_main(void* p) { + UNUSED(p); + + /* Allocate all of the necessary structures */ + ExampleThermoContext* context = example_thermo_context_alloc(); + + /* Start the applicaton's main loop. It won't return until the application was requested to exit. */ + example_thermo_run(context); + + /* Release all unneeded resources */ + example_thermo_context_free(context); + + return 0; +} diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3d527f306c2d325fc72380856f14860f51b3742c GIT binary patch literal 7293 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 literal 0 HcmV?d00001 diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index fab1ccf05..9ddb079dc 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -271,7 +271,7 @@ void onewire_cli_print_usage() { static void onewire_cli_search(Cli* cli) { UNUSED(cli); - OneWireHost* onewire = onewire_host_alloc(); + OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio); uint8_t address[8]; bool done = false; diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index ff6dbc239..025b605fc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.2,, +Version,+,13.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -910,6 +910,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index dafeefdd0..41cc80bfb 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -199,3 +199,27 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + // TODO: describe second ROW + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index ef2cdae7f..a24afbf94 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -111,6 +111,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a1f34f106..35985aebb 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.2,, +Version,+,13.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1094,6 +1094,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1129,20 +1130,13 @@ Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uin Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" -Function,+,furi_hal_ibutton_add_interrupt,void,"GpioExtiCallback, void*" Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t Function,+,furi_hal_ibutton_emulate_start,void,"uint32_t, FuriHalIbuttonEmulateCallback, void*" Function,+,furi_hal_ibutton_emulate_stop,void, Function,-,furi_hal_ibutton_init,void, -Function,+,furi_hal_ibutton_pin_get_level,_Bool, -Function,+,furi_hal_ibutton_pin_high,void, -Function,+,furi_hal_ibutton_pin_low,void, -Function,+,furi_hal_ibutton_remove_interrupt,void, -Function,+,furi_hal_ibutton_start_drive,void, -Function,+,furi_hal_ibutton_start_drive_in_isr,void, -Function,+,furi_hal_ibutton_start_interrupt,void, -Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, -Function,+,furi_hal_ibutton_stop,void, +Function,+,furi_hal_ibutton_pin_configure,void, +Function,+,furi_hal_ibutton_pin_reset,void, +Function,+,furi_hal_ibutton_pin_write,void,const _Bool Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t @@ -2040,7 +2034,7 @@ Function,+,onewire_device_detach,void,OneWireDevice* Function,+,onewire_device_free,void,OneWireDevice* Function,+,onewire_device_get_id_p,uint8_t*,OneWireDevice* Function,+,onewire_device_send_id,void,OneWireDevice* -Function,+,onewire_host_alloc,OneWireHost*, +Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* Function,+,onewire_host_read,uint8_t,OneWireHost* Function,+,onewire_host_read_bit,_Bool,OneWireHost* @@ -2054,7 +2048,7 @@ Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool" -Function,+,onewire_slave_alloc,OneWireSlave*, +Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin* Function,+,onewire_slave_attach,void,"OneWireSlave*, OneWireDevice*" Function,+,onewire_slave_detach,void,OneWireSlave* Function,+,onewire_slave_free,void,OneWireSlave* diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index a148a3af2..e8318afcf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #define GET_SYSCFG_EXTI_PORT(gpio) \ @@ -224,85 +225,85 @@ static void furi_hal_gpio_int_call(uint16_t pin_num) { /* Interrupt handlers */ void EXTI0_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); furi_hal_gpio_int_call(0); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); } } void EXTI1_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); furi_hal_gpio_int_call(1); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); } } void EXTI2_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); furi_hal_gpio_int_call(2); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); } } void EXTI3_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); furi_hal_gpio_int_call(3); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); } } void EXTI4_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); furi_hal_gpio_int_call(4); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); } } void EXTI9_5_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); furi_hal_gpio_int_call(5); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); furi_hal_gpio_int_call(6); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); furi_hal_gpio_int_call(7); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); furi_hal_gpio_int_call(8); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); furi_hal_gpio_int_call(9); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); } } void EXTI15_10_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); furi_hal_gpio_int_call(10); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); furi_hal_gpio_int_call(11); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); furi_hal_gpio_int_call(12); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); furi_hal_gpio_int_call(13); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); furi_hal_gpio_int_call(14); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); furi_hal_gpio_int_call(15); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index 0375893e7..c05cd69a8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -89,47 +89,16 @@ void furi_hal_ibutton_emulate_stop() { } } -void furi_hal_ibutton_start_drive() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_configure() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_start_drive_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_start_interrupt() { - furi_hal_ibutton_pin_high(); - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_ibutton_start_interrupt_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_stop() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_reset() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) { - furi_hal_gpio_add_int_callback(&ibutton_gpio, cb, context); -} - -void furi_hal_ibutton_remove_interrupt() { - furi_hal_gpio_remove_int_callback(&ibutton_gpio); -} - -void furi_hal_ibutton_pin_low() { - furi_hal_gpio_write(&ibutton_gpio, false); -} - -void furi_hal_ibutton_pin_high() { - furi_hal_gpio_write(&ibutton_gpio, true); -} - -bool furi_hal_ibutton_pin_get_level() { - return furi_hal_gpio_read(&ibutton_gpio); +void furi_hal_ibutton_pin_write(const bool state) { + furi_hal_gpio_write(&ibutton_gpio, state); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h index fb57d628b..b723fe176 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h @@ -7,7 +7,6 @@ #include #include -#include #ifdef __cplusplus extern "C" { @@ -18,70 +17,43 @@ typedef void (*FuriHalIbuttonEmulateCallback)(void* context); /** Initialize */ void furi_hal_ibutton_init(); +/** + * Start emulation timer + * @param period timer period + * @param callback timer callback + * @param context callback context + */ void furi_hal_ibutton_emulate_start( uint32_t period, FuriHalIbuttonEmulateCallback callback, void* context); +/** + * Update emulation timer period + * @param period new timer period + */ void furi_hal_ibutton_emulate_set_next(uint32_t period); +/** + * Stop emulation timer + */ void furi_hal_ibutton_emulate_stop(); /** - * Sets the pin to normal mode (open collector), and sets it to float + * Set the pin to normal mode (open collector), and sets it to float */ -void furi_hal_ibutton_start_drive(); - -/** - * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_drive_in_isr(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float - */ -void furi_hal_ibutton_start_interrupt(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_interrupt_in_isr(); +void furi_hal_ibutton_pin_configure(); /** * Sets the pin to analog mode, and sets it to float */ -void furi_hal_ibutton_stop(); +void furi_hal_ibutton_pin_reset(); /** - * Attach interrupt callback to iButton pin - * @param cb callback - * @param context context + * iButton write pin + * @param state true / false */ -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context); - -/** - * Remove interrupt callback from iButton pin - */ -void furi_hal_ibutton_remove_interrupt(); - -/** - * Sets the pin to low - */ -void furi_hal_ibutton_pin_low(); - -/** - * Sets the pin to high (float in iButton pin modes) - */ -void furi_hal_ibutton_pin_high(); - -/** - * Get pin level - * @return true if level is high - * @return false if level is low - */ -bool furi_hal_ibutton_pin_get_level(); +void furi_hal_ibutton_pin_write(const bool state); #ifdef __cplusplus } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 03cc6e714..c0eb9ee67 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -191,3 +191,26 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 51ea7bcc1..04b99a84e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -216,6 +216,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index b0238f79f..145e49df2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -77,7 +77,7 @@ void furi_hal_rfid_init() { void furi_hal_rfid_pins_reset() { // ibutton bus disable - furi_hal_ibutton_stop(); + furi_hal_ibutton_pin_reset(); // pulldown rfid antenna furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -94,8 +94,8 @@ void furi_hal_rfid_pins_reset() { void furi_hal_rfid_pins_emulate() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // pull pin to timer out furi_hal_gpio_init_ex( @@ -115,8 +115,8 @@ void furi_hal_rfid_pins_emulate() { void furi_hal_rfid_pins_read() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // dont pull rfid antenna furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 9d5ea3897..1fe39b5e5 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -25,8 +25,8 @@ iButtonWorker* ibutton_worker_alloc() { iButtonWorker* worker = malloc(sizeof(iButtonWorker)); worker->key_p = NULL; worker->key_data = malloc(ibutton_key_get_max_size()); - worker->host = onewire_host_alloc(); - worker->slave = onewire_slave_alloc(); + worker->host = onewire_host_alloc(&ibutton_gpio); + worker->slave = onewire_slave_alloc(&ibutton_gpio); worker->writer = ibutton_writer_alloc(worker->host); worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index b284940e7..da6b10766 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -234,16 +234,13 @@ void ibutton_worker_emulate_timer_cb(void* context) { furi_assert(context); iButtonWorker* worker = context; - LevelDuration level = + const LevelDuration level_duration = protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); - furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level)); + const bool level = level_duration_get_level(level_duration); - if(level_duration_get_level(level)) { - furi_hal_ibutton_pin_high(); - } else { - furi_hal_ibutton_pin_low(); - } + furi_hal_ibutton_emulate_set_next(level); + furi_hal_ibutton_pin_write(level); } void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { @@ -266,7 +263,7 @@ void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); - furi_hal_ibutton_start_drive(); + furi_hal_ibutton_pin_configure(); furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); } diff --git a/lib/one_wire/one_wire_host.c b/lib/one_wire/one_wire_host.c index 654b9e45d..f3d3d3c4d 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -1,18 +1,19 @@ #include -#include + #include "one_wire_host.h" #include "one_wire_host_timing.h" struct OneWireHost { - // global search state - unsigned char saved_rom[8]; + const GpioPin* gpio_pin; + unsigned char saved_rom[8]; /** < global search state */ uint8_t last_discrepancy; uint8_t last_family_discrepancy; bool last_device_flag; }; -OneWireHost* onewire_host_alloc() { +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) { OneWireHost* host = malloc(sizeof(OneWireHost)); + host->gpio_pin = gpio_pin; onewire_host_reset_search(host); return host; } @@ -23,49 +24,47 @@ void onewire_host_free(OneWireHost* host) { } bool onewire_host_reset(OneWireHost* host) { - UNUSED(host); uint8_t r; uint8_t retries = 125; // wait until the gpio is high - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); do { if(--retries == 0) return 0; furi_delay_us(2); - } while(!furi_hal_ibutton_pin_get_level()); + } while(!furi_hal_gpio_read(host->gpio_pin)); // pre delay furi_delay_us(OWH_RESET_DELAY_PRE); // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_RESET_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_RESET_RELEASE); // read and post delay - r = !furi_hal_ibutton_pin_get_level(); + r = !furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_RESET_DELAY_POST); return r; } bool onewire_host_read_bit(OneWireHost* host) { - UNUSED(host); bool result; // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_READ_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_READ_RELEASE); // read and post delay - result = furi_hal_ibutton_pin_get_level(); + result = furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_READ_DELAY_POST); return result; @@ -90,22 +89,21 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) } void onewire_host_write_bit(OneWireHost* host, bool value) { - UNUSED(host); if(value) { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_1_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_1_RELEASE); } else { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_0_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_0_RELEASE); } } @@ -123,13 +121,13 @@ void onewire_host_skip(OneWireHost* host) { } void onewire_host_start(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_start_drive(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void onewire_host_stop(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_stop(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void onewire_host_reset_search(OneWireHost* host) { @@ -150,7 +148,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { host->last_device_flag = false; } -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) { +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { uint8_t id_bit_number; uint8_t last_zero, rom_byte_number, search_result; uint8_t id_bit, cmp_id_bit; @@ -259,7 +257,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSear host->last_family_discrepancy = 0; search_result = false; } else { - for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i]; + for(int i = 0; i < 8; i++) new_addr[i] = host->saved_rom[i]; } return search_result; diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index 5b55ee8dd..9c01abc11 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -22,10 +22,10 @@ typedef struct OneWireHost OneWireHost; /** * Allocate onewire host bus - * @param gpio + * @param pin * @return OneWireHost* */ -OneWireHost* onewire_host_alloc(); +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); /** * Deallocate onewire host bus @@ -114,7 +114,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); * @param mode * @return uint8_t */ -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); #ifdef __cplusplus } diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index ad9c34b19..4b54c4f99 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -27,6 +27,7 @@ typedef enum { } OneWireSlaveError; struct OneWireSlave { + const GpioPin* gpio_pin; OneWireSlaveError error; OneWireDevice* device; OneWireSlaveResultCallback result_cb; @@ -35,15 +36,15 @@ struct OneWireSlave { /*********************** PRIVATE ***********************/ -uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { - UNUSED(bus); +static uint32_t + onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { uint32_t start = DWT->CYCCNT; uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); uint32_t time_captured; do { //-V1044 time_captured = DWT->CYCCNT; - if(furi_hal_ibutton_pin_get_level() != pin_value) { + if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { uint32_t remaining_time = time_ticks - (time_captured - start); remaining_time /= furi_hal_cortex_instructions_per_microsecond(); return remaining_time; @@ -53,14 +54,14 @@ uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, cons return 0; } -bool onewire_slave_show_presence(OneWireSlave* bus) { +static bool onewire_slave_show_presence(OneWireSlave* bus) { // wait while master delay presence check onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); // show presence - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(bus->gpio_pin, false); furi_delay_us(OWS_PRESENCE_MIN); - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); // somebody also can show presence const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; @@ -74,7 +75,7 @@ bool onewire_slave_show_presence(OneWireSlave* bus) { return true; } -bool onewire_slave_receive_bit(OneWireSlave* bus) { +static bool onewire_slave_receive_bit(OneWireSlave* bus) { // wait while bus is low uint32_t time = OWS_SLOT_MAX; time = onewire_slave_wait_while_gpio_is(bus, time, false); @@ -98,7 +99,7 @@ bool onewire_slave_receive_bit(OneWireSlave* bus) { return (time > 0); } -bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { +static bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { const bool write_zero = !value; // wait while bus is low @@ -119,7 +120,7 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { // choose write time if(write_zero) { - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(bus->gpio_pin, false); time = OWS_WRITE_ZERO; } else { time = OWS_READ_MAX; @@ -127,12 +128,12 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { // hold line for ZERO or ONE time furi_delay_us(time); - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); return true; } -void onewire_slave_cmd_search_rom(OneWireSlave* bus) { +static void onewire_slave_cmd_search_rom(OneWireSlave* bus) { const uint8_t key_bytes = 8; uint8_t* key = onewire_device_get_id_p(bus->device); @@ -151,7 +152,7 @@ void onewire_slave_cmd_search_rom(OneWireSlave* bus) { } } -bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { +static bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { uint8_t cmd; onewire_slave_receive(bus, &cmd, 1); @@ -178,14 +179,14 @@ bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { } } -bool onewire_slave_bus_start(OneWireSlave* bus) { +static bool onewire_slave_bus_start(OneWireSlave* bus) { bool result = true; if(bus->device == NULL) { result = false; } else { FURI_CRITICAL_ENTER(); - furi_hal_ibutton_start_drive_in_isr(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); bus->error = NO_ERROR; if(onewire_slave_show_presence(bus)) { @@ -197,7 +198,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { result = false; } - furi_hal_ibutton_start_interrupt_in_isr(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); FURI_CRITICAL_EXIT(); } @@ -207,7 +208,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { static void exti_cb(void* context) { OneWireSlave* bus = context; - volatile bool input_state = furi_hal_ibutton_pin_get_level(); + volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); static uint32_t pulse_start = 0; if(input_state) { @@ -234,8 +235,9 @@ static void exti_cb(void* context) { /*********************** PUBLIC ***********************/ -OneWireSlave* onewire_slave_alloc() { +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { OneWireSlave* bus = malloc(sizeof(OneWireSlave)); + bus->gpio_pin = gpio_pin; bus->error = NO_ERROR; bus->device = NULL; bus->result_cb = NULL; @@ -249,14 +251,15 @@ void onewire_slave_free(OneWireSlave* bus) { } void onewire_slave_start(OneWireSlave* bus) { - furi_hal_ibutton_add_interrupt(exti_cb, bus); - furi_hal_ibutton_start_interrupt(); + furi_hal_gpio_add_int_callback(bus->gpio_pin, exti_cb, bus); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); } void onewire_slave_stop(OneWireSlave* bus) { - UNUSED(bus); - furi_hal_ibutton_stop(); - furi_hal_ibutton_remove_interrupt(); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_remove_int_callback(bus->gpio_pin); } void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { @@ -282,7 +285,7 @@ void onewire_slave_set_result_callback( bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { uint8_t bytes_sent = 0; - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); // bytes loop for(; bytes_sent < data_length; ++bytes_sent) { @@ -304,7 +307,7 @@ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { uint8_t bytes_received = 0; - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); for(; bytes_received < data_length; ++bytes_received) { uint8_t value = 0; diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 82e9f5523..2e5db3a1c 100644 --- a/lib/one_wire/one_wire_slave.h +++ b/lib/one_wire/one_wire_slave.h @@ -19,10 +19,10 @@ typedef void (*OneWireSlaveResultCallback)(void* context); /** * Allocate onewire slave - * @param pin + * @param gpio_pin * @return OneWireSlave* */ -OneWireSlave* onewire_slave_alloc(); +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); /** * Free onewire slave From 111c7557b3c3c5a371f3fddf1a63592b7ad04b6c Mon Sep 17 00:00:00 2001 From: Liam Hays Date: Wed, 8 Feb 2023 01:08:50 -0700 Subject: [PATCH 32/75] Fix minor UI inconsistencies and bugs (#2361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changed blue LED to cyan in NFC Magic and Picopass apps. * Fix capitalization of ATQA and UID in NFC Add Manually wizard. * Fix reselection of "Saved" menu item in NFC and RFID apps. * Fix double back press after deleting a file in the SubGhz browser. * Make NFC app behave like other apps: return to the file browser after deleting a file. * Rename NfcSceneSetAtqua to NfcSceneSetAtqa. * Save selected menu items in NFC Magic and Picopass apps in a way that always works. * Restore previous selection in Universal Remotes menu. * Other way to do universal remote menu saving, and NFC Extra Actions saves last selection. Co-authored-by: あく --- .../main/infrared/scenes/infrared_scene_universal.c | 4 +++- .../main/lfrfid/scenes/lfrfid_scene_start.c | 9 ++++++++- applications/main/nfc/scenes/nfc_scene_config.h | 2 +- .../main/nfc/scenes/nfc_scene_delete_success.c | 2 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 ++ applications/main/nfc/scenes/nfc_scene_set_atqa.c | 2 +- applications/main/nfc/scenes/nfc_scene_set_sak.c | 2 +- applications/main/nfc/scenes/nfc_scene_set_uid.c | 2 +- applications/main/nfc/scenes/nfc_scene_start.c | 13 ++++++++++++- .../subghz/scenes/subghz_scene_delete_success.c | 5 ++++- applications/plugins/nfc_magic/nfc_magic.c | 6 +++--- .../nfc_magic/scenes/nfc_magic_scene_start.c | 10 +++++++++- applications/plugins/picopass/picopass.c | 6 +++--- .../plugins/picopass/scenes/picopass_scene_start.c | 7 ++++++- 14 files changed, 55 insertions(+), 17 deletions(-) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 914360d78..5043c9bd7 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -33,7 +33,8 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalAC, infrared_scene_universal_submenu_callback, context); - submenu_set_selected_item(submenu, 0); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); } @@ -54,6 +55,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; } + scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 8e1c92dbb..2d83ba53b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Like in the other apps, explicitly save the scene state + // in each branch in case the user cancels loading a file. + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved); furi_string_set(app->file_path, LFRFID_APP_FOLDER); scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); consumed = true; } else if(event.event == SubmenuIndexExtraActions) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions); scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); consumed = true; } - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event); } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index ce51d000d..f81fe178e 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -4,7 +4,7 @@ ADD_SCENE(nfc, saved_menu, SavedMenu) ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, set_type, SetType) ADD_SCENE(nfc, set_sak, SetSak) -ADD_SCENE(nfc, set_atqa, SetAtqua) +ADD_SCENE(nfc, set_atqa, SetAtqa) ADD_SCENE(nfc, set_uid, SetUid) ADD_SCENE(nfc, generate_info, GenerateInfo) ADD_SCENE(nfc, read_card_success, ReadCardSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 1664a9e5b..795363527 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -30,7 +30,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { nfc->scene_manager, NfcSceneMfClassicKeys); } else { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + nfc->scene_manager, NfcSceneFileSelect); } } } diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 717e8efc4..66aaf5a26 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -34,6 +34,8 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_set_atqa.c b/applications/main/nfc/scenes/nfc_scene_set_atqa.c index f2100aa19..d079b3804 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_atqa.c +++ b/applications/main/nfc/scenes/nfc_scene_set_atqa.c @@ -11,7 +11,7 @@ void nfc_scene_set_atqa_on_enter(void* context) { // Setup view ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter atqa in hex"); + byte_input_set_header_text(byte_input, "Enter ATQA in hex"); byte_input_set_result_callback( byte_input, nfc_scene_set_atqa_byte_input_callback, diff --git a/applications/main/nfc/scenes/nfc_scene_set_sak.c b/applications/main/nfc/scenes/nfc_scene_set_sak.c index 3c88f3504..60a1e1494 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_sak.c +++ b/applications/main/nfc/scenes/nfc_scene_set_sak.c @@ -28,7 +28,7 @@ bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 5f0f52f6e..54606b68e 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -11,7 +11,7 @@ void nfc_scene_set_uid_on_enter(void* context) { // Setup view ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter uid in hex"); + byte_input_set_header_text(byte_input, "Enter UID in hex"); nfc->dev_edit_data = nfc->dev->dev_data.nfc_data; byte_input_set_result_callback( byte_input, diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 2a116fe09..a01f871ab 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -48,11 +48,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); nfc->dev->dev_data.read_mode = NfcReadModeAuto; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { nfc_device_data_clear(&nfc->dev->dev_data); @@ -63,19 +66,27 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Save the scene state explicitly in each branch, so that + // if the user cancels loading a file, the Saved menu item + // is properly reselected. + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); consumed = true; } else if(event.event == SubmenuIndexExtraAction) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction); scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); consumed = true; } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; } else if(event.event == SubmenuIndexDebug) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); } return consumed; } diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4f98b6a39..8a2546243 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -31,7 +31,10 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); + // Commented so that the user doesn't have to press + // back twice to get to the main SubGhz menu after + // deleting a file. + //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/plugins/nfc_magic/nfc_magic.c index e4e0ffde0..1805f35ed 100644 --- a/applications/plugins/nfc_magic/nfc_magic.c +++ b/applications/plugins/nfc_magic/nfc_magic.c @@ -136,9 +136,9 @@ void nfc_magic_free(NfcMagic* nfc_magic) { free(nfc_magic); } -static const NotificationSequence nfc_magic_sequence_blink_start_blue = { +static const NotificationSequence nfc_magic_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -149,7 +149,7 @@ static const NotificationSequence nfc_magic_sequence_blink_stop = { }; void nfc_magic_blink_start(NfcMagic* nfc_magic) { - notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue); + notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_cyan); } void nfc_magic_blink_stop(NfcMagic* nfc_magic) { diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c index f2984443f..a70eb8acc 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c @@ -40,16 +40,24 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexCheck) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); consumed = true; } else if(event.event == SubmenuIndexWriteGen1A) { + // Explicitly save state in each branch so that the + // correct option is reselected if the user cancels + // loading a file. + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); consumed = true; } else if(event.event == SubmenuIndexWipe) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); consumed = true; } - scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart, event.event); } return consumed; diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c index e9f48b676..217f963d3 100644 --- a/applications/plugins/picopass/picopass.c +++ b/applications/plugins/picopass/picopass.c @@ -137,9 +137,9 @@ void picopass_text_store_clear(Picopass* picopass) { memset(picopass->text_store, 0, sizeof(picopass->text_store)); } -static const NotificationSequence picopass_sequence_blink_start_blue = { +static const NotificationSequence picopass_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -150,7 +150,7 @@ static const NotificationSequence picopass_sequence_blink_stop = { }; void picopass_blink_start(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); + notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); } void picopass_blink_stop(Picopass* picopass) { diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/plugins/picopass/scenes/picopass_scene_start.c index 6d1aeedcd..d33a1d264 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_start.c +++ b/applications/plugins/picopass/scenes/picopass_scene_start.c @@ -32,13 +32,18 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead); scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Explicitly save state so that the correct item is + // reselected if the user cancels loading a file. + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); consumed = true; } - scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); } return consumed; From 23ecc186c24062002aeac5e73235b3a6c0021f77 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:26:17 +0300 Subject: [PATCH 33/75] Custom font set function (#2261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * custom font set function * update API symbols * add example of custom font usage * delete u8g2 dependency in example custom font * rename to canvas_set_custom_u8g2_font * now change the name in ALL places Co-authored-by: あく Co-authored-by: hedger --- .../debug/example_custom_font/application.fam | 9 ++ .../example_custom_font/example_custom_font.c | 98 +++++++++++++++++++ applications/services/gui/canvas.c | 6 ++ applications/services/gui/canvas.h | 7 ++ firmware/targets/f18/api_symbols.csv | 5 +- firmware/targets/f7/api_symbols.csv | 1 + 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 applications/debug/example_custom_font/application.fam create mode 100644 applications/debug/example_custom_font/example_custom_font.c diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam new file mode 100644 index 000000000..02285b8a0 --- /dev/null +++ b/applications/debug/example_custom_font/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_custom_font", + name="Example: custom font", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_custom_font_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c new file mode 100644 index 000000000..15eeb5f02 --- /dev/null +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +//This arrays contains the font itself. You can use any u8g2 font you want + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310fb, 1); + u8g2_SetFont(&canvas->fb, font); +} + void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 0b0c7e658..b2a065ca7 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -146,6 +146,13 @@ void canvas_invert_color(Canvas* canvas); */ void canvas_set_font(Canvas* canvas, Font font); +/** Set custom drawing font + * + * @param canvas Canvas instance + * @param font Pointer to u8g2 const uint8_t* font array + */ +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); + /** Draw string at position of baseline defined by x, y. * * @param canvas Canvas instance diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 025b605fc..d29d696fc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.0,, +Version,+,13.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -546,6 +546,7 @@ Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" @@ -910,7 +911,6 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* -Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1017,6 +1017,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 35985aebb..6456555d8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -632,6 +632,7 @@ Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" Function,+,canvas_set_font,void,"Canvas*, Font" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" Function,+,canvas_width,uint8_t,Canvas* From 00076deece707d61aed1f9cc01151b430b4f3a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Such=C3=A1nek?= Date: Wed, 8 Feb 2023 10:35:38 +0100 Subject: [PATCH 34/75] SCons: do not include backup files in build (#2221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SCons: do not include backup files in build * fbt: now GlobRecursive by default excludes backup files * fbt: added backup file exclusion to plain libs Signed-off-by: Michal Suchanek Co-authored-by: hedger Co-authored-by: hedger Co-authored-by: あく --- assets/SConscript | 4 +++- firmware.scons | 3 ++- lib/misc.scons | 8 +++++++- scripts/fbt/util.py | 4 ++++ scripts/fbt_tools/fbt_assets.py | 4 +--- scripts/fbt_tools/sconsrecursiveglob.py | 8 ++++++-- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/assets/SConscript b/assets/SConscript index ef5d83c79..21437aa30 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -78,7 +78,9 @@ if assetsenv["IS_BASE_FIRMWARE"]: resources = assetsenv.Command( "#/assets/resources/Manifest", assetsenv.GlobRecursive( - "*", assetsenv.Dir("resources").srcnode(), exclude="Manifest" + "*", + assetsenv.Dir("resources").srcnode(), + exclude=["Manifest"], ), action=Action( '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}" --timestamp=${GIT_UNIX_TIMESTAMP}', diff --git a/firmware.scons b/firmware.scons index 92f2d1a91..a094765af 100644 --- a/firmware.scons +++ b/firmware.scons @@ -2,6 +2,7 @@ Import("ENV", "fw_build_meta") from SCons.Errors import UserError from SCons.Node import FS + import itertools from fbt_extra.util import ( @@ -171,7 +172,7 @@ sources = [apps_c] # Gather sources only from app folders in current configuration sources.extend( itertools.chain.from_iterable( - fwenv.GlobRecursive(source_type, appdir.relpath, exclude="lib") + fwenv.GlobRecursive(source_type, appdir.relpath, exclude=["lib"]) for appdir, source_type in fwenv["APPBUILD"].get_builtin_app_folders() ) ) diff --git a/lib/misc.scons b/lib/misc.scons index cd2377ceb..49b6b61d9 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -1,5 +1,7 @@ Import("env") +from fbt.util import GLOB_FILE_EXCLUSION + env.Append( CPPPATH=[ "#/lib/digital_signal", @@ -39,7 +41,11 @@ libs_plain = [ ] for lib in libs_plain: - sources += Glob(lib + "/*.c*", source=True) + sources += Glob( + lib + "/*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, + ) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ee7562058..7bdaea031 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -8,6 +8,10 @@ import os WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") +# Used by default when globbing for files with GlobRecursive +# Excludes all files ending with ~, usually created by editors as backup files +GLOB_FILE_EXCLUSION = ["*~"] + def tempfile_arg_esc_func(arg): arg = quote_spaces(arg) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index e17487358..d2a58f3fb 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -27,9 +27,7 @@ def proto_emitter(target, source, env): def dolphin_emitter(target, source, env): res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"]) source = [res_root_dir] - source.extend( - env.GlobRecursive("*.*", res_root_dir.srcnode()), - ) + source.extend(env.GlobRecursive("*.*", res_root_dir.srcnode())) target_base_dir = target[0] env.Replace(_DOLPHIN_OUT_DIR=target[0]) diff --git a/scripts/fbt_tools/sconsrecursiveglob.py b/scripts/fbt_tools/sconsrecursiveglob.py index 32ff55ea1..fbcee965b 100644 --- a/scripts/fbt_tools/sconsrecursiveglob.py +++ b/scripts/fbt_tools/sconsrecursiveglob.py @@ -1,7 +1,11 @@ import SCons +from SCons.Script import Flatten +from fbt.util import GLOB_FILE_EXCLUSION -def GlobRecursive(env, pattern, node=".", exclude=None): +def GlobRecursive(env, pattern, node=".", exclude=[]): + exclude = list(set(Flatten(exclude) + GLOB_FILE_EXCLUSION)) + # print(f"Starting glob for {pattern} from {node} (exclude: {exclude})") results = [] if isinstance(node, str): node = env.Dir(node) @@ -13,7 +17,7 @@ def GlobRecursive(env, pattern, node=".", exclude=None): source=True, exclude=exclude, ) - # print(f"Glob for {pattern} from {node}: {results}") + # print(f"Glob result for {pattern} from {node}: {results}") return results From 20f98050f2030e3a8f1509421cc43729617919b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 19:38:09 +0900 Subject: [PATCH 35/75] Github: disable f18 build (#2375) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 080e7c2c2..a6c9219c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: pull_request: env: - TARGETS: f7 f18 + TARGETS: f7 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work From cee9b640b30502f65ab1081134a891a1e6841343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 20:45:33 +0900 Subject: [PATCH 36/75] Update Missing SD Card icon from PR 2373 (#2376) --- assets/icons/SDCard/SDQuestion_35x43.png | Bin 1950 -> 2042 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/SDCard/SDQuestion_35x43.png b/assets/icons/SDCard/SDQuestion_35x43.png index 9b9c9a58e3257f926677533f8cc99ffb19dd74f5..257ab1d852460ad83dffa5989e765902fcb09578 100644 GIT binary patch delta 523 zcmbQo|BHWugar#T0}w3HVF!|o#X;^)4C~IxyaaMs(j9#r85lP9bN@+X1@hSfd_r9R z|NjqUh90_HH&Ic7@%uz8ezSl|hu0(V4SA9t0%7SFZhTNGdi(6U zqtX3c^NQF~FPUjxUAjEGb!OR09>>O*+iqu6ZhUtYncXoe)wdUX*U5dow13$b cea!*}8+O&+FAqd3fX-m>boFyt=akR{04GeM8vp%dtcP1$Xn~K~5UtcSa z%;J*#qDngjo1&C7tKif^yUBA|b~EXNDDla)tXmZHfpYpVNgxwl-{k*nGF%YtPKoJu zlNYnuaYJ>0`G45tb)bBg)Z*l#%z~24{5+VUK`w3}W`z~d*xb~TL_3Aaf$YNd+7>6) z0X=Zo)5S3)qV?@GZ@$9{JgkPN{;vPCYk}`2GmG;QF`|a6IRxAKa|IY)wstHrWSDT* z#4q^7^nZ)qe+@L{O>(>JubebcTr>xA;mo&QS_`}msD%H&0j`ym{bIX}= zOW&%!e!SiJ%|z?^88d$Te0N6PAoTmLgw@r0!CkAW-_AH!8nnmtgxFKVBQwS7oqSd? aS1_D$pUltxg^3;L9|liXKbLh*2~7Y$MyGH9 From 99253a0e28f45476d631ac8db8c8717d13e0bac8 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:20:42 +0400 Subject: [PATCH 37/75] [FL-3093, FL-3087] SubGhz: Fix Raw write, add short duration filter setting (#2300) * SubGhz: Fix recording RAW files, sometimes could not start at a high level * SubGhz: subghz_worker, add short duration filter setting * SubGhz: capture raw timings in cli. Furi: clear pending interrupts on ISR set/reset * SubGhz: fix start duration in furi_hal_subghz_start_async_rx * [FL-3093] SubGhz: hopping issue in some regions * [FL-3087] SubGhz: fix delete-ok issue * SubGhz: remove copypasta from rx_raw cli command Co-authored-by: Aleksandr Kutuzov --- .../scenes/subghz_scene_delete_success.c | 5 -- .../scenes/subghz_scene_receiver_info.c | 15 ++++ applications/main/subghz/subghz_cli.c | 83 ++++++++++++++++++- firmware/targets/f7/api_symbols.csv | 3 +- .../targets/f7/furi_hal/furi_hal_interrupt.c | 17 ++++ .../targets/f7/furi_hal/furi_hal_subghz.c | 11 ++- lib/subghz/protocols/raw.c | 1 + lib/subghz/subghz_worker.c | 36 ++++---- lib/subghz/subghz_worker.h | 8 ++ 9 files changed, 150 insertions(+), 29 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 8a2546243..4d9f33e37 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -28,13 +28,8 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event if(event.event == SubGhzCustomEventSceneDeleteSuccess) { if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReadRAW)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - // Commented so that the user doesn't have to press - // back twice to get to the main SubGhz menu after - // deleting a file. - //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index c0f901578..4733b0e1d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -129,6 +129,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + } + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, + furi_string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); + } + if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { + subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + } + subghz->state_notifications = SubGhzNotificationStateRx; } else { subghz->state_notifications = SubGhzNotificationStateTx; } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 536cb535e..9271443a8 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -309,6 +309,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { free(instance); } +void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + // Configure radio + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelDuration level_duration; + size_t counter = 0; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == 0) { + continue; + } + if(ret != sizeof(LevelDuration)) { + puts("stream corrupt"); + break; + } + if(level_duration_is_reset(level_duration)) { + puts(". "); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + printf("%c%lu ", level ? '+' : '-', duration); + } + furi_thread_stdout_flush(); + counter++; + if(counter > 255) { + puts("\r\n"); + counter = 0; + } + } + + // Shutdown radio + furi_hal_subghz_stop_async_rx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + + // Cleanup + furi_stream_buffer_free(instance->stream); + free(instance); +} void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -431,7 +506,8 @@ static void subghz_cli_command_print_usage() { printf("\tchat \t - Chat with other Flippers\r\n"); printf( "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Reception key\r\n"); + printf("\trx \t - Receive\r\n"); + printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -733,6 +809,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "rx_raw") == 0) { + subghz_cli_command_rx_raw(cli, args, context); + break; + } + if(furi_string_cmp_str(cmd, "decode_raw") == 0) { subghz_cli_command_decode_raw(cli, args, context); break; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6456555d8..65ab375ef 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.0,, +Version,+,13.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2675,6 +2675,7 @@ Function,+,subghz_worker_free,void,SubGhzWorker* Function,+,subghz_worker_is_running,_Bool,SubGhzWorker* Function,+,subghz_worker_rx_callback,void,"_Bool, uint32_t, void*" Function,+,subghz_worker_set_context,void,"SubGhzWorker*, void*" +Function,+,subghz_worker_set_filter,void,"SubGhzWorker*, uint16_t" Function,+,subghz_worker_set_overrun_callback,void,"SubGhzWorker*, SubGhzWorkerOverrunCallback" Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPairCallback" Function,+,subghz_worker_start,void,SubGhzWorker* diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index 1b1132d0c..b5639d230 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -74,6 +74,21 @@ __attribute__((always_inline)) static inline void NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]); } +__attribute__((always_inline)) static inline void + furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { + NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_get_pending(FuriHalInterruptId index) { + NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_set_pending(FuriHalInterruptId index) { + NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + __attribute__((always_inline)) static inline void furi_hal_interrupt_disable(FuriHalInterruptId index) { NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]); @@ -123,6 +138,7 @@ void furi_hal_interrupt_set_isr_ex( // Pre ISR clear furi_assert(furi_hal_interrupt_isr[index].isr != NULL); furi_hal_interrupt_disable(index); + furi_hal_interrupt_clear_pending(index); } furi_hal_interrupt_isr[index].isr = isr; @@ -131,6 +147,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set + furi_hal_interrupt_clear_pending(index); furi_hal_interrupt_enable(index, priority); } else { // Post ISR clear diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index a6d275308..596464613 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -438,7 +438,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; // Clock division for capture filter LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -455,13 +455,15 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); // Timer: channel 2 direct LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); + LL_TIM_IC_SetFilter( + TIM2, + LL_TIM_CHANNEL_CH2, + LL_TIM_IC_FILTER_FDIV32_N8); // Capture filter: 1/(64000000/64/4/32*8) = 16us // ISR setup furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); @@ -481,6 +483,9 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* // Switch to RX furi_hal_subghz_rx(); + + //Clear the variable after the end of the session + furi_hal_subghz_capture_delta_duration = 0; } void furi_hal_subghz_stop_async_rx() { diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index b639c93b9..ac3492e78 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -159,6 +159,7 @@ bool subghz_protocol_raw_save_to_file_init( instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); instance->file_is_open = RAWFileIsOpenWrite; instance->sample_write = 0; + instance->last_level = false; instance->pause = false; init = true; } while(0); diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 35e399885..50b5aba51 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -12,7 +12,6 @@ struct SubGhzWorker { volatile bool overrun; LevelDuration filter_level_duration; - bool filter_running; uint16_t filter_duration; SubGhzWorkerOverrunCallback overrun_callback; @@ -59,24 +58,19 @@ static int32_t subghz_worker_thread_callback(void* context) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); - if(instance->filter_running) { - if((duration < instance->filter_duration) || - (instance->filter_level_duration.level == level)) { - instance->filter_level_duration.duration += duration; + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; - } else if(instance->filter_level_duration.level != level) { - if(instance->pair_callback) - instance->pair_callback( - instance->context, - instance->filter_level_duration.level, - instance->filter_level_duration.duration); - - instance->filter_level_duration.duration = duration; - instance->filter_level_duration.level = level; - } - } else { + } else if(instance->filter_level_duration.level != level) { if(instance->pair_callback) - instance->pair_callback(instance->context, level, duration); + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; } } } @@ -94,8 +88,7 @@ SubGhzWorker* subghz_worker_alloc() { instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); - //setting filter - instance->filter_running = true; + //setting default filter in us instance->filter_duration = 30; return instance; @@ -149,3 +142,8 @@ bool subghz_worker_is_running(SubGhzWorker* instance) { furi_assert(instance); return instance->running; } + +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { + furi_assert(instance); + instance->filter_duration = timeout; +} \ No newline at end of file diff --git a/lib/subghz/subghz_worker.h b/lib/subghz/subghz_worker.h index f85b1fdc7..657278f50 100644 --- a/lib/subghz/subghz_worker.h +++ b/lib/subghz/subghz_worker.h @@ -67,6 +67,14 @@ void subghz_worker_stop(SubGhzWorker* instance); */ bool subghz_worker_is_running(SubGhzWorker* instance); +/** + * Short duration filter setting. + * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled + * @param instance Pointer to a SubGhzWorker instance + * @param timeout time in us + */ +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); + #ifdef __cplusplus } #endif From b1f581239bbc83af1720e7180e6bf4a7c319288e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:01:00 +0300 Subject: [PATCH 38/75] BadUSB: Keyboard Layouts (#2256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BadUSB: Keyboard Layouts * Apply requested changes pt1 * Add layout file check when we loading config Co-authored-by: Nikolay Minaylov Co-authored-by: あく --- applications/main/bad_usb/bad_usb_app.c | 72 +++++++++++++- applications/main/bad_usb/bad_usb_app_i.h | 11 ++- applications/main/bad_usb/bad_usb_script.c | 51 ++++++++-- applications/main/bad_usb/bad_usb_script.h | 2 + .../main/bad_usb/bad_usb_settings_filename.h | 3 + .../bad_usb/scenes/bad_usb_scene_config.c | 53 ++++++++++ .../bad_usb/scenes/bad_usb_scene_config.h | 2 + .../scenes/bad_usb_scene_config_layout.c | 50 ++++++++++ .../scenes/bad_usb_scene_file_select.c | 14 ++- .../main/bad_usb/scenes/bad_usb_scene_work.c | 27 ++++-- .../main/bad_usb/views/bad_usb_view.c | 91 ++++++++++++------ .../main/bad_usb/views/bad_usb_view.h | 6 +- assets/icons/Archive/keyboard_10px.png | Bin 0 -> 147 bytes .../resources/badusb/assets/layouts/ba-BA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/cz_CS.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/da-DA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-DE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/dvorak.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-UK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-US.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/es-ES.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-BE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-FR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hr-HR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hu-HU.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/it-IT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nb-NO.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nl-NL.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-BR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-PT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/si-SI.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sk-SK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sv-SE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/tr-TR.kl | Bin 0 -> 256 bytes 36 files changed, 323 insertions(+), 59 deletions(-) create mode 100644 applications/main/bad_usb/bad_usb_settings_filename.h create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c create mode 100644 assets/icons/Archive/keyboard_10px.png create mode 100644 assets/resources/badusb/assets/layouts/ba-BA.kl create mode 100644 assets/resources/badusb/assets/layouts/cz_CS.kl create mode 100644 assets/resources/badusb/assets/layouts/da-DA.kl create mode 100644 assets/resources/badusb/assets/layouts/de-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/de-DE.kl create mode 100644 assets/resources/badusb/assets/layouts/dvorak.kl create mode 100644 assets/resources/badusb/assets/layouts/en-UK.kl create mode 100644 assets/resources/badusb/assets/layouts/en-US.kl create mode 100644 assets/resources/badusb/assets/layouts/es-ES.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-BE.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-FR.kl create mode 100644 assets/resources/badusb/assets/layouts/hr-HR.kl create mode 100644 assets/resources/badusb/assets/layouts/hu-HU.kl create mode 100644 assets/resources/badusb/assets/layouts/it-IT.kl create mode 100644 assets/resources/badusb/assets/layouts/nb-NO.kl create mode 100644 assets/resources/badusb/assets/layouts/nl-NL.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-BR.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-PT.kl create mode 100644 assets/resources/badusb/assets/layouts/si-SI.kl create mode 100755 assets/resources/badusb/assets/layouts/sk-SK.kl create mode 100644 assets/resources/badusb/assets/layouts/sv-SE.kl create mode 100644 assets/resources/badusb/assets/layouts/tr-TR.kl diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 6fd29cd70..5f2aa4789 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -1,9 +1,12 @@ #include "bad_usb_app_i.h" +#include "bad_usb_settings_filename.h" #include #include #include #include +#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME + static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); BadUsbApp* app = context; @@ -22,15 +25,62 @@ static void bad_usb_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } +static void bad_usb_load_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + furi_string_push_back(app->keyboard_layout, chr); + } + } else { + furi_string_reset(app->keyboard_layout); + } + storage_file_close(settings_file); + storage_file_free(settings_file); + + if(!furi_string_empty(app->keyboard_layout)) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } +} + +static void bad_usb_save_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + storage_file_write( + settings_file, + furi_string_get_cstr(app->keyboard_layout), + furi_string_size(app->keyboard_layout)); + storage_file_write(settings_file, "\n", 1); + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); - app->file_path = furi_string_alloc(); + app->bad_usb_script = NULL; + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } + bad_usb_load_settings(app); + app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); app->dialogs = furi_record_open(RECORD_DIALOGS); @@ -53,6 +103,10 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + app->bad_usb_view = bad_usb_alloc(); view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); @@ -64,9 +118,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { if(!furi_string_empty(app->file_path)) { + app->bad_usb_script = bad_usb_script_open(app->file_path); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); } else { - furi_string_set(app->file_path, BAD_USB_APP_PATH_FOLDER); + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); } } @@ -77,6 +133,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { void bad_usb_app_free(BadUsbApp* app) { furi_assert(app); + if(app->bad_usb_script) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + } + // Views view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -85,6 +146,10 @@ void bad_usb_app_free(BadUsbApp* app) { view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); widget_free(app->widget); + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + submenu_free(app->submenu); + // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); @@ -94,7 +159,10 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); + bad_usb_save_settings(app); + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); free(app); } diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index f333c36d8..1bd1964c8 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -15,8 +15,10 @@ #include #include "views/bad_usb_view.h" -#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb") -#define BAD_USB_APP_EXTENSION ".txt" +#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") +#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" typedef enum { BadUsbAppErrorNoFiles, @@ -30,9 +32,11 @@ struct BadUsbApp { NotificationApp* notifications; DialogsApp* dialogs; Widget* widget; + Submenu* submenu; BadUsbAppError error; FuriString* file_path; + FuriString* keyboard_layout; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; }; @@ -40,4 +44,5 @@ struct BadUsbApp { typedef enum { BadUsbAppViewError, BadUsbAppViewWork, -} BadUsbAppView; + BadUsbAppViewConfig, +} BadUsbAppView; \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index e2281133f..1416acfee 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -16,6 +16,9 @@ #define SCRIPT_STATE_END (-2) #define SCRIPT_STATE_NEXT_LINE (-3) +#define BADUSB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + typedef enum { WorkerEvtToggle = (1 << 0), WorkerEvtEnd = (1 << 1), @@ -28,6 +31,7 @@ struct BadUsbScript { BadUsbState st; FuriString* file_path; uint32_t defdelay; + uint16_t layout[128]; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -205,10 +209,10 @@ static bool ducky_altstring(const char* param) { return state; } -static bool ducky_string(const char* param) { +static bool ducky_string(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; while(param[i] != '\0') { - uint16_t keycode = HID_ASCII_TO_KEY(param[i]); + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); @@ -218,7 +222,7 @@ static bool ducky_string(const char* param) { return true; } -static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { +static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && @@ -227,7 +231,7 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { } } if((accept_chars) && (strlen(param) > 0)) { - return (HID_ASCII_TO_KEY(param[0]) & 0xFF); + return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF); } return 0; } @@ -276,7 +280,7 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_string(line_tmp); + state = ducky_string(bad_usb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid string %s", line_tmp); } @@ -312,14 +316,14 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint16_t key = ducky_get_keycode(line_tmp, true); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true); furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_hid_kb_press(key); furi_hal_hid_kb_release_all(); return (0); } else { // Special keys + modifiers - uint16_t key = ducky_get_keycode(line_tmp, false); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false); if(key == HID_KEYBOARD_NONE) { if(error != NULL) { snprintf(error, error_len, "No keycode defined for %s", line_tmp); @@ -329,7 +333,7 @@ static int32_t if((key & 0xFF00) != 0) { // It's a modifier key line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(line_tmp, true); + key |= ducky_get_keycode(bad_usb, line_tmp, true); } furi_hal_hid_kb_press(key); furi_hal_hid_kb_release(key); @@ -650,12 +654,19 @@ static int32_t bad_usb_worker(void* context) { return 0; } +static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); + memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +} + BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); + bad_usb_script_set_default_keyboard_layout(bad_usb); bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; @@ -674,6 +685,30 @@ void bad_usb_script_close(BadUsbScript* bad_usb) { free(bad_usb); } +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { + furi_assert(bad_usb); + + if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_usb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_usb_script_set_default_keyboard_layout(bad_usb); + } + storage_file_free(layout_file); +} + void bad_usb_script_toggle(BadUsbScript* bad_usb) { furi_assert(bad_usb); furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index 188142db8..1e4d98fe7 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -33,6 +33,8 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path); void bad_usb_script_close(BadUsbScript* bad_usb); +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); + void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h new file mode 100644 index 000000000..12ba8f31c --- /dev/null +++ b/applications/main/bad_usb/bad_usb_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings" diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c new file mode 100644 index 000000000..2a9f2f76c --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -0,0 +1,53 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum SubmenuIndex { + SubmenuIndexKeyboardLayout, +}; + +void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_add_item( + submenu, + "Keyboard layout", + SubmenuIndexKeyboardLayout, + bad_usb_scene_config_submenu_callback, + bad_usb); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); +} + +bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + consumed = true; + if(event.event == SubmenuIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else { + furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_reset(submenu); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 0ab8f54f8..423aedc51 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -1,3 +1,5 @@ ADD_SCENE(bad_usb, file_select, FileSelect) ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) +ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_layout, ConfigLayout) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c new file mode 100644 index 000000000..7708ed1d8 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -0,0 +1,50 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_usb_layout_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_usb->keyboard_layout)) { + furi_string_set(predefined_path, bad_usb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_usb_scene_config_layout_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb_layout_select(bad_usb)) { + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + } + scene_manager_previous_scene(bad_usb->scene_manager); +} + +bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadUsbApp* bad_usb = context; + return false; +} + +void bad_usb_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadUsbApp* bad_usb = context; +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index f1f34f5bc..b04669252 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -7,8 +7,10 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { furi_assert(bad_usb); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px); - browser_options.base_path = BAD_USB_APP_PATH_FOLDER; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( @@ -21,12 +23,18 @@ void bad_usb_scene_file_select_on_enter(void* context) { BadUsbApp* bad_usb = context; furi_hal_usb_disable(); + if(bad_usb->bad_usb_script) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = NULL; + } if(bad_usb_file_select(bad_usb)) { + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { furi_hal_usb_enable(); - //scene_manager_previous_scene(bad_usb->scene_manager); view_dispatcher_stop(bad_usb->view_dispatcher); } } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 1e3534822..187b83bd9 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -4,10 +4,10 @@ #include #include "toolbox/path.h" -void bad_usb_scene_work_ok_callback(InputType type, void* context) { +void bad_usb_scene_work_button_callback(InputKey key, void* context) { furi_assert(context); BadUsbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, type); + view_dispatcher_send_custom_event(app->view_dispatcher, key); } bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { @@ -15,8 +15,13 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - bad_usb_script_toggle(app->bad_usb_script); - consumed = true; + if(event.event == InputKeyLeft) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + consumed = true; + } else if(event.event == InputKeyOk) { + bad_usb_script_toggle(app->bad_usb_script); + consumed = true; + } } else if(event.type == SceneManagerEventTypeTick) { bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); } @@ -28,20 +33,22 @@ void bad_usb_scene_work_on_enter(void* context) { FuriString* file_name; file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); - app->bad_usb_script = bad_usb_script_open(app->file_path); - furi_string_free(file_name); + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app); + bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); } void bad_usb_scene_work_on_exit(void* context) { - BadUsbApp* app = context; - bad_usb_script_close(app->bad_usb_script); + UNUSED(context); } diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index b3eb9bb56..bb9dc3b7e 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,5 +1,6 @@ #include "bad_usb_view.h" #include "../bad_usb_script.h" +#include #include #include @@ -7,12 +8,13 @@ struct BadUsb { View* view; - BadUsbOkCallback callback; + BadUsbButtonCallback callback; void* context; }; typedef struct { char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; BadUsbState state; uint8_t anim_frame; } BadUsbModel; @@ -25,9 +27,23 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_reset(disp_str); + furi_string_push_back(disp_str, '('); + for(size_t i = 0; i < strlen(model->layout); i++) + furi_string_push_back(disp_str, model->layout[i]); + furi_string_push_back(disp_str, ')'); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { @@ -38,23 +54,28 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_button_center(canvas, "Cancel"); } + if((model->state.state == BadUsbStateNotConnected) || + (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + elements_button_left(canvas, "Config"); + } + if(model->state.state == BadUsbStateNotConnected) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); } else if(model->state.state == BadUsbStateWillRun) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); } else if(model->state.state == BadUsbStateScriptError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); canvas_set_font(canvas, FontSecondary); @@ -64,49 +85,49 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { furi_string_reset(disp_str); canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); } else if(model->state.state == BadUsbStateIdle) { - canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateRunning) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDone) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100"); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDelay) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); canvas_set_font(canvas, FontSecondary); furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); } else { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } furi_string_free(disp_str); @@ -118,10 +139,10 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { + if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { consumed = true; furi_assert(bad_usb->callback); - bad_usb->callback(InputTypeShort, bad_usb->context); + bad_usb->callback(event->key, bad_usb->context); } } @@ -151,7 +172,7 @@ View* bad_usb_get_view(BadUsb* bad_usb) { return bad_usb->view; } -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) { +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) { furi_assert(bad_usb); furi_assert(callback); with_view_model( @@ -174,6 +195,14 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { true); } +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { + furi_assert(layout); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); +} void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { furi_assert(st); with_view_model( diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h index 80a47e2ca..8447fb055 100644 --- a/applications/main/bad_usb/views/bad_usb_view.h +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -4,7 +4,7 @@ #include "../bad_usb_script.h" typedef struct BadUsb BadUsb; -typedef void (*BadUsbOkCallback)(InputType type, void* context); +typedef void (*BadUsbButtonCallback)(InputKey key, void* context); BadUsb* bad_usb_alloc(); @@ -12,8 +12,10 @@ void bad_usb_free(BadUsb* bad_usb); View* bad_usb_get_view(BadUsb* bad_usb); -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context); +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context); void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); + void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/assets/icons/Archive/keyboard_10px.png b/assets/icons/Archive/keyboard_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..74a10e6db2e784486a6781c0db8346373b9c7409 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasB`QKad%E=yDy9;wgN=z( mnGc5;aHhO3XW%q4U|?wcz_Hx?k)a$=ErX}4pUXO@geCyYPARJZ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/ba-BA.kl b/assets/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHkcVK4j(2-*&PMujecVX$$ zm1{Rv)^6RqGmA3vIK<6U^hikQGaw^p$cQl$rpzdqv!GYm`cD?ePm*T4GZVB*m| zTln^pGi$gvZr&YZh&>B_Ym yx9;4VM-@dp{CKxUod!)>wCT{LN1p*hMvR#-WyYKZOIEDeuw}=dEbdXpBj69fFAeVi literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-CH.kl b/assets/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 0000000000000000000000000000000000000000..1704bc9dbaae9fcc90213cb33b0132bca1b71866 GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5i<8!$oa;st31l>ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-DE.kl b/assets/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 0000000000000000000000000000000000000000..67b05c042f1649ab6aa17c4d369b096c88ec2785 GIT binary patch literal 256 zcmaLLHx9y30KiboqW2Pd4WuBN!ay1SVRHX7Ftgz88Gbu+;q<%y>dMxg4`L4uK0?Afwu&6#5(8&;hd+qL7+fvJ5TwhjIGHgM$E s3wH`~pT4NjqeYV%GsZ+Ts4^j@ONSu?Qu;h-6Y}9rz=$sw?-ss^@Y|jPyWjO!M`q5wk-2p3gN<8r z&%QYE=EIg5a~5PQS+OQ(!`(YC&%FKkuw>baRci*;Z5Z0LW!sLCv58$%d-feTbmZ8H tQ!f-;S$XnFi82+c)Cj24Af!o)HXR~j61t@H=rdr*h%pnUTr9kL6TT^X3*7(! literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/en-US.kl b/assets/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 0000000000000000000000000000000000000000..8089d8257881765fe67691a206b6bf6e9cb2c97d GIT binary patch literal 256 zcmaLL#}dH+007aQ9YGK+QKLn)QG!wb|I3_nvA4HS?#PKldHvOyg-=&zuHE=#=iaS_ zZ!UcJvS-1P6&Y)CHf-7P@WI=Hk6)Ko46ItSZfL`%EhF1@?Ao($Y~sM7BgamhnmTjt sl{+_bFTSV{P^CtlkOoa!M6~J9rAMEbgaJcFjF~VcWyYMVcW=Ig9}}MoYybcN literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/es-ES.kl b/assets/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 0000000000000000000000000000000000000000..15e9d7997c3f5178982fe212b5341f09469486bc GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5lA#$@e+Y@}VF{DLgOx;B0)}2=l z9xW`m^W?$a6A39BGPdm4bKuCsyDv_@yqZSIvK6b=tlO|@%eEc6_Ut=w=*Y1Xr_P+a zaOujmdHhEaxleOSl&MgqMx6#tTD0lVrAMCuLq?35FlEM^1xr?}`HLiOkp;W~w>u5X literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-BE.kl b/assets/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 0000000000000000000000000000000000000000..ea9e553e894a470639ee48648a386898ed5cfa67 GIT binary patch literal 256 zcmaLLNm9Z96adk#F6Mcj2}uy4P%#!3Wcd%1`=7xz8+-Q}Uc2(a<#qk5PfnzyQSfU* zM$Q+7r6X=Ue0nzVU}|RW(Kk=N`{Y4IN#)2x%&gn6Y0I`9yY}omaOmB!6Q|akyKw2s zwR^Yj+?YodMSOGfCuY=X(4ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-FR.kl b/assets/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 0000000000000000000000000000000000000000..f9193297e58722fd4f1547cb9ef62474beb2487b GIT binary patch literal 256 zcmaLLH*Nv}6adlDh@5i{8(2U#EVi(~}3$%7XqF9)7tV%3^;8#Zm(wqw_xeeVt(Ikw`|nR6E| z-MV(;$}Ebw#*KnQOsG<$PJ<>b+H~mBqtBfoBgRyiGGoqyC0o{PSdm5^W$Xj~0L-fm A_W%F@ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/hr-HR.kl b/assets/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk)x=fVv{{L5{w#jy9;k{43_;_Fc>YIyakMR>% z$I*KC;Silq2439!rR7Y=g)29D?mT$;j68aqcv{8C#IX~nre@BZyKw2s+`_dROSkUa zd$6+hXyeH=%E+RN!9GSLjF~VcC1b{%1xr@s6s*}$vSr7f0~Iw#8crlpM-gqnA2aL@ AGynhq literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/it-IT.kl b/assets/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 0000000000000000000000000000000000000000..059e428808a07b26d0da37d7f37d5d8d4614365b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>eeY5fH(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvm0r4@+k!5$Wx$5i83J(6{^&z)1XO96ECB#K@^L=Pq0t xyK-$Ft`y%!)Nxcub+H~j=&?BVJfFUEsOqeobPMlv%@{{ldJ?{*0 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/pt-BR.kl b/assets/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 0000000000000000000000000000000000000000..d36421cfc45747687dba3cabe05ff84a4550050a GIT binary patch literal 256 zcmaKnM-ssR004J(1W9z!q7!8hWkx%CjQ_uZtBbwu-WGn@v1j|6{@Q_wH)qaWn0aIE z#+66!wV)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk17S!45fq0~DYekR@*l?gpTRX7d-ob{d*zLn+x4&B`SxrR z=Qx?hB0l}{*Orbe7e0Kp_0GKq?|ty-qfcJ^p^Fo{=&Yh=Vrt)knYlwpj-5DlX5rk0 zrAt??-B?-MxN~b3W#qAstHmjD0& literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/sv-SE.kl b/assets/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 0000000000000000000000000000000000000000..5c55bb9ef1df3785fb143eb463a5375be10ce96f GIT binary patch literal 256 zcmaLLH*x|26u?l8hMaSTC2U}9uLl~S{D;~5pMjbNbx-iw55HVL*H`^JB+m2m(_8mBMwCT{LN1p*hMvR#-WyYKZOIEDeuw}=dG^!}#7VrkLXASNE literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/tr-TR.kl b/assets/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 0000000000000000000000000000000000000000..6377b770365ed544824108765ce2587f315bc1da GIT binary patch literal 256 zcmaLLM{dFZ6adlDh~A6oU~F)Sk{HDcB$od$x&IlmW|P^yf%iW7;^Te&t8Y@DWRb_s zyH`sq8**D7yeOY}m46&w(Q+&Ri(sH|qEa_ygc8 B4s!qi literal 0 HcmV?d00001 From 8288a08eb3ab1c49451c1abcf1bc38bbfdcc8c71 Mon Sep 17 00:00:00 2001 From: Brandon Weeks Date: Wed, 8 Feb 2023 07:26:45 -0800 Subject: [PATCH 39/75] SubGhz: add protocol "Linear Delta-3" (#2239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add protocol "Linear Delta-3" * SubGhz: fix Leniar Delta 3 * BadUSB: mask pvs studio warning for valid code Co-authored-by: SkorP Co-authored-by: あく Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- .../debug/unit_tests/subghz/subghz_test.c | 18 +- applications/main/bad_usb/bad_usb_script.c | 2 +- assets/unit_tests/subghz/linear_delta3.sub | 7 + .../unit_tests/subghz/linear_delta3_raw.sub | 8 + assets/unit_tests/subghz/test_random_raw.sub | 2 + lib/subghz/protocols/linear_delta3.c | 359 ++++++++++++++++++ lib/subghz/protocols/linear_delta3.h | 111 ++++++ lib/subghz/protocols/protocol_items.c | 49 ++- lib/subghz/protocols/protocol_items.h | 1 + 9 files changed, 543 insertions(+), 14 deletions(-) create mode 100644 assets/unit_tests/subghz/linear_delta3.sub create mode 100644 assets/unit_tests/subghz/linear_delta3_raw.sub create mode 100644 lib/subghz/protocols/linear_delta3.c create mode 100644 lib/subghz/protocols/linear_delta3.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 1dee1d59e..83fadeda9 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 273 +#define TEST_RANDOM_COUNT_PARSE 295 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -489,6 +489,14 @@ MU_TEST(subghz_decoder_linear_test) { "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_decoder_linear_delta3_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"), + SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME), + "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_decoder_megacode_test) { mu_assert( subghz_decoder_test( @@ -647,6 +655,12 @@ MU_TEST(subghz_encoder_linear_test) { "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_encoder_linear_delta3_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")), + "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_encoder_megacode_test) { mu_assert( subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), @@ -772,6 +786,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_somfy_telis_test); MU_RUN_TEST(subghz_decoder_star_line_test); MU_RUN_TEST(subghz_decoder_linear_test); + MU_RUN_TEST(subghz_decoder_linear_delta3_test); MU_RUN_TEST(subghz_decoder_megacode_test); MU_RUN_TEST(subghz_decoder_secplus_v1_test); MU_RUN_TEST(subghz_decoder_secplus_v2_test); @@ -796,6 +811,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_nice_flo_test); MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_encoder_linear_test); + MU_RUN_TEST(subghz_encoder_linear_delta3_test); MU_RUN_TEST(subghz_encoder_megacode_test); MU_RUN_TEST(subghz_encoder_holtek_test); MU_RUN_TEST(subghz_encoder_secplus_v1_test); diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 1416acfee..0fadbcc07 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -694,7 +694,7 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou } File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { + if(!furi_string_empty(layout_path)) { //-V1051 if(storage_file_open( layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint16_t layout[128]; diff --git a/assets/unit_tests/subghz/linear_delta3.sub b/assets/unit_tests/subghz/linear_delta3.sub new file mode 100644 index 000000000..f00507428 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: LinearDelta3 +Bit: 8 +Key: 00 00 00 00 00 00 00 D0 diff --git a/assets/unit_tests/subghz/linear_delta3_raw.sub b/assets/unit_tests/subghz/linear_delta3_raw.sub new file mode 100644 index 000000000..1973622a5 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index be635f04d..7571d688d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -173,3 +173,5 @@ RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 RAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178 RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362 RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 2022e9c47..a1f1bc149 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,19 +1,44 @@ #include "protocol_items.h" const SubGhzProtocol* subghz_protocol_registry_items[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, - &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_smc5326, + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &subghz_protocol_chamb_code, + &subghz_protocol_power_smart, + &subghz_protocol_marantec, + &subghz_protocol_bett, + &subghz_protocol_doitrand, + &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell_wdb, + &subghz_protocol_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 998fb56b3..185e47394 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -21,6 +21,7 @@ #include "gate_tx.h" #include "raw.h" #include "linear.h" +#include "linear_delta3.h" #include "secplus_v2.h" #include "secplus_v1.h" #include "megacode.h" From 0afc4a8982265d432a473b26cc94973f105973ca Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:37:24 +0400 Subject: [PATCH 40/75] [FL-3092] SubGhz: add DOOYA protocol (#2178) * SubGhz: add DOOYA protocol * SubGhz: add unit_test DOOYA protocol * SubGhz: fix protocol Dooya Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 17 +- assets/unit_tests/subghz/dooya.sub | 7 + assets/unit_tests/subghz/dooya_raw.sub | 8 + assets/unit_tests/subghz/test_random_raw.sub | 3 + lib/subghz/protocols/dooya.c | 447 ++++++++++++++++++ lib/subghz/protocols/dooya.h | 107 +++++ lib/subghz/protocols/protocol_items.c | 3 +- lib/subghz/protocols/protocol_items.h | 1 + 8 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 assets/unit_tests/subghz/dooya.sub create mode 100644 assets/unit_tests/subghz/dooya_raw.sub create mode 100644 lib/subghz/protocols/dooya.c create mode 100644 lib/subghz/protocols/dooya.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 83fadeda9..0dba19d90 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 295 +#define TEST_RANDOM_COUNT_PARSE 300 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -612,6 +612,13 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) { "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_decoder_dooya_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -757,6 +764,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) { "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_encoder_dooya_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -803,6 +816,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -828,6 +842,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_encoder_smc5326_test); MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_encoder_dooya_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/assets/unit_tests/subghz/dooya.sub b/assets/unit_tests/subghz/dooya.sub new file mode 100644 index 000000000..0767a1a73 --- /dev/null +++ b/assets/unit_tests/subghz/dooya.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Dooya +Bit: 40 +Key: 00 00 00 E1 DC 03 05 11 diff --git a/assets/unit_tests/subghz/dooya_raw.sub b/assets/unit_tests/subghz/dooya_raw.sub new file mode 100644 index 000000000..6c3ca1627 --- /dev/null +++ b/assets/unit_tests/subghz/dooya_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 7571d688d..aee15a16d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -175,3 +175,6 @@ RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 12 RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/lib/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/lib/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index a1f1bc149..99de18c93 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -39,8 +39,9 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, }; const SubGhzProtocolRegistry subghz_protocol_registry = { .items = subghz_protocol_registry_items, - .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 185e47394..4d1551bb3 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -39,5 +39,6 @@ #include "ansonic.h" #include "smc5326.h" #include "holtek_ht12x.h" +#include "dooya.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From 31259d530499b2ff6e07fd24a5ec3b8583a41ac0 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:59:49 +0400 Subject: [PATCH 41/75] [FL-3091] SubGhz: add protocol Alutech at-4n (#2352) * GubGhz: add protocol Alutech at-4n * SubGhz: fix syntax * SubGhz: fix subghz_protocol_decoder_alutech_at_4n_get_hash_data * SubGhz: add unit test alutech at-4n * SubGhz: add name key Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 14 +- applications/main/subghz/subghz.c | 2 + applications/main/subghz/subghz_cli.c | 4 + assets/resources/subghz/assets/alutech_at_4n | 6 + .../unit_tests/subghz/alutech_at_4n_raw.sub | 10 + assets/unit_tests/subghz/test_random_raw.sub | 5 + firmware/targets/f7/api_symbols.csv | 2 + lib/subghz/environment.c | 16 + lib/subghz/environment.h | 17 + lib/subghz/protocols/alutech_at_4n.c | 455 ++++++++++++++++++ lib/subghz/protocols/alutech_at_4n.h | 74 +++ lib/subghz/protocols/came_atomo.c | 2 +- lib/subghz/protocols/keeloq.c | 13 +- lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + 15 files changed, 617 insertions(+), 5 deletions(-) create mode 100644 assets/resources/subghz/assets/alutech_at_4n create mode 100644 assets/unit_tests/subghz/alutech_at_4n_raw.sub create mode 100644 lib/subghz/protocols/alutech_at_4n.c create mode 100644 lib/subghz/protocols/alutech_at_4n.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 0dba19d90..705d6f2f6 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -12,8 +12,9 @@ #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") +#define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 300 +#define TEST_RANDOM_COUNT_PARSE 304 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -43,6 +44,8 @@ static void subghz_test_init(void) { environment_handler, CAME_ATOMO_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment_handler, NICE_FLOR_S_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment_handler, ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_protocol_registry( environment_handler, (void*)&subghz_protocol_registry); @@ -619,6 +622,14 @@ MU_TEST(subghz_decoder_dooya_test) { "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); } +MU_TEST(subghz_decoder_alutech_at_4n_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"), + SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -817,6 +828,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_decoder_dooya_test); + MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index b7564ab57..200be6262 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -187,6 +187,8 @@ SubGhz* subghz_alloc() { subghz->txrx->environment = subghz_environment_alloc(); subghz_environment_set_came_atomo_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry( diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 9271443a8..ac19d65b4 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -257,6 +257,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -452,6 +454,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); diff --git a/assets/resources/subghz/assets/alutech_at_4n b/assets/resources/subghz/assets/alutech_at_4n new file mode 100644 index 000000000..5d7beacec --- /dev/null +++ b/assets/resources/subghz/assets/alutech_at_4n @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz Keystore RAW File +Version: 0 +Encryption: 1 +IV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31 +Encrypt_data: RAW +E811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60 \ No newline at end of file diff --git a/assets/unit_tests/subghz/alutech_at_4n_raw.sub b/assets/unit_tests/subghz/alutech_at_4n_raw.sub new file mode 100644 index 000000000..ae5db9715 --- /dev/null +++ b/assets/unit_tests/subghz/alutech_at_4n_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index aee15a16d..44e1613c7 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -178,3 +178,8 @@ RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 65ab375ef..846041c3e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2569,12 +2569,14 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* +Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 0a4b7b606..b39b259d4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -6,6 +6,7 @@ struct SubGhzEnvironment { const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; }; SubGhzEnvironment* subghz_environment_alloc() { @@ -57,6 +58,21 @@ const char* return instance->came_atomo_rainbow_table_file_name; } +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + void subghz_environment_set_nice_flor_s_rainbow_table_file_name( SubGhzEnvironment* instance, const char* filename) { diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index e994f7c97..7bd38ba2f 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -52,6 +52,23 @@ void subghz_environment_set_came_atomo_rainbow_table_file_name( */ const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + /** * Set filename to work with Nice Flor-S. * @param instance Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 3f6045bea..2e2718485 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -189,7 +189,7 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file - * @param number_atomo_magic_xor Сell number in the array + * @param number_atomo_magic_xor number in the array * @return atomo_magic_xor */ static uint64_t subghz_protocol_came_atomo_get_magic_xor_in_file( diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 6a9c3468e..4a3602fbe 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -390,11 +390,14 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur subghz_protocol_keeloq_const.te_delta)) { // Found end TX instance->decoder.parser_step = KeeloqDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -411,6 +414,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( @@ -421,6 +426,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else { diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 99de18c93..3904c1811 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -40,6 +40,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 4d1551bb3..cd9b7c6ed 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -40,5 +40,6 @@ #include "smc5326.h" #include "holtek_ht12x.h" #include "dooya.h" +#include "alutech_at_4n.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From bf4d00a7d1cb94a2b17d26647359ab93207880c1 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:20:28 +0400 Subject: [PATCH 42/75] [FL-3100] SubGhz: add protocol Nice One (#2358) * SubGhz: add protocol Nice One * SubGhz: fix annotation * SubGhz: add unit test Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 10 +- assets/unit_tests/subghz/nice_one_raw.sub | 12 ++ assets/unit_tests/subghz/test_random_raw.sub | 7 + lib/subghz/protocols/nice_flor_s.c | 167 +++++++++++++++--- 4 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 assets/unit_tests/subghz/nice_one_raw.sub diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 705d6f2f6..97629efea 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -14,7 +14,7 @@ #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 304 +#define TEST_RANDOM_COUNT_PARSE 317 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -630,6 +630,13 @@ MU_TEST(subghz_decoder_alutech_at_4n_test) { "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n"); } +MU_TEST(subghz_decoder_nice_one_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -829,6 +836,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); + MU_RUN_TEST(subghz_decoder_nice_one_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/assets/unit_tests/subghz/nice_one_raw.sub b/assets/unit_tests/subghz/nice_one_raw.sub new file mode 100644 index 000000000..169b3f088 --- /dev/null +++ b/assets/unit_tests/subghz/nice_one_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 44e1613c7..12b08d48d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -183,3 +183,10 @@ RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 80 RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 411ceeacf..dd5521a64 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -14,6 +14,9 @@ #define TAG "SubGhzProtocoNiceFlorS" +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { .te_short = 500, .te_long = 1000, @@ -28,6 +31,7 @@ struct SubGhzProtocolDecoderNiceFlorS { SubGhzBlockGeneric generic; const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; }; struct SubGhzProtocolEncoderNiceFlorS { @@ -77,6 +81,64 @@ const SubGhzProtocol subghz_protocol_nice_flor_s = { .encoder = &subghz_protocol_nice_flor_s_encoder, }; +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -237,10 +299,14 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -274,6 +340,11 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ } else { instance->decoder.parser_step = NiceFlorSDecoderStepReset; } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } break; } } @@ -287,6 +358,7 @@ static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name) { /* + * Protocol Nice Flor-S * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; * P1 (4-bit) - batch repetition number, calculated by the formula: @@ -307,6 +379,24 @@ static void subghz_protocol_nice_flor_s_remote_controller( * data => 0x1c5783607f7b3 key serial cnt * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * */ if(!file_name) { instance->cnt = 0; @@ -333,7 +423,15 @@ bool subghz_protocol_decoder_nice_flor_s_serialize( SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; } bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { @@ -344,11 +442,25 @@ bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperForma if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if(instance->generic.data_count_bit != - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + ret = true; } while(false); return ret; @@ -360,20 +472,33 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } } From 39841bd5a9ad1d6b1f4d222ab0670be96aadc732 Mon Sep 17 00:00:00 2001 From: itsweekly <92674764+itsweekly@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:28:34 +0100 Subject: [PATCH 43/75] Universal Projector Remote (#2343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Georgii Surkov Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 2 +- .../infrared/scenes/infrared_scene_config.h | 1 + .../scenes/infrared_scene_universal.c | 12 +- .../infrared_scene_universal_projector.c | 86 ++ assets/resources/infrared/assets/projector.ir | 829 ++++++++++++++++++ documentation/UniversalRemotes.md | 7 + 6 files changed, 935 insertions(+), 2 deletions(-) create mode 100644 applications/main/infrared/scenes/infrared_scene_universal_projector.c create mode 100644 assets/resources/infrared/assets/projector.ir diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5f5e2d4bb..3fa99cb02 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) { printf("\tir universal \r\n"); printf("\tir universal list \r\n"); // TODO: Do not hardcode universal remote names - printf("\tAvailable universal remotes: tv audio ac\r\n"); + printf("\tAvailable universal remotes: tv audio ac projector\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 111fd2d31..27eabe225 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -17,6 +17,7 @@ ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, universal_ac, UniversalAC) ADD_SCENE(infrared, universal_audio, UniversalAudio) +ADD_SCENE(infrared, universal_projector, UniversalProjector) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 5043c9bd7..4ef7c5c26 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -4,6 +4,7 @@ typedef enum { SubmenuIndexUniversalTV, SubmenuIndexUniversalAC, SubmenuIndexUniversalAudio, + SubmenuIndexUniversalProjector, } SubmenuIndex; static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { @@ -27,6 +28,12 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalAudio, infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Projectors", + SubmenuIndexUniversalProjector, + infrared_scene_universal_submenu_callback, + context); submenu_add_item( submenu, "Air Conditioners", @@ -54,6 +61,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexUniversalAudio) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; + } else if(event.event == SubmenuIndexUniversalProjector) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector); + consumed = true; } scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } @@ -64,4 +74,4 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { void infrared_scene_universal_on_exit(void* context) { Infrared* infrared = context; submenu_reset(infrared->submenu); -} +} \ No newline at end of file diff --git a/applications/main/infrared/scenes/infrared_scene_universal_projector.c b/applications/main/infrared/scenes/infrared_scene_universal_projector.c new file mode 100644 index 000000000..c1df91c34 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_projector.c @@ -0,0 +1,86 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_projector_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projector.ir")); + + button_panel_reserve(button_panel, 2, 2); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 19, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 19, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 66, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 66, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + + button_panel_add_label(button_panel, 2, 11, FontPrimary, "Proj. remote"); + button_panel_add_label(button_panel, 17, 62, FontSecondary, "Volume"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_projector_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/assets/resources/infrared/assets/projector.ir b/assets/resources/infrared/assets/projector.ir new file mode 100644 index 000000000..e9861de21 --- /dev/null +++ b/assets/resources/infrared/assets/projector.ir @@ -0,0 +1,829 @@ +Filetype: IR library file +Version: 1 +# +# Model: Smart +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8A 00 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +# Model: Hitatchi +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 +# +name: Vol_up +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 49 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 44 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 83 7C 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 82 7D 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 13 00 00 +command: 87 78 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 +# +name: Mute +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 29 D6 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 08 F7 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 04 FB 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 93 6C 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 1D 00 00 00 +# +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 +# AV-Mute +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 4C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 529 7218 126 6585 219 703 180 5362 427 18618 177 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620 +# ON +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 46 B9 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0E F1 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0D F2 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 08 16 00 00 +command: C8 37 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 28 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 29 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1D E2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 99 66 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 98 67 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 52 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 51 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 56 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 5A 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 82 7D 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 83 7C 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 91 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 31 00 00 00 +command: D0 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 89 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 30 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 31 00 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 32 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9031 4479 596 560 572 558 564 566 566 1693 571 1688 566 563 569 561 571 559 563 1696 568 561 571 559 563 1697 567 562 570 1689 565 1694 570 1688 566 1693 571 1661 593 1693 571 558 564 566 566 564 568 1691 563 541 591 564 568 562 570 560 562 1697 567 1692 562 1696 568 562 570 1689 565 564 568 561 571 559 563 567 565 564 568 562 570 560 562 567 565 1694 570 1689 565 1694 570 1688 566 1693 571 1688 566 1693 571 1662 592 13987 9031 2220 590 96231 9034 2217 593 96234 9030 2222 588 96247 9037 2215 595 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 14 00 00 00 +# OFF +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 1D 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 15 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: Vol_up +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: Vol_dn +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 18 E9 00 00 +command: 49 B6 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 14 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 40 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1F E0 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 81 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8F 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: A8 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 88 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 9C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 50 AF 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635 +# +name: Power +type: parsed +protocol: NECext +address: FF FF 00 00 +command: E8 17 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: FF FF 00 00 +command: BD 42 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: FF FF 00 00 +command: F2 0D 00 00 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +# +name: Vol_dn +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 71 01 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 81 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 \ No newline at end of file diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md index 264829e16..325f640d7 100644 --- a/documentation/UniversalRemotes.md +++ b/documentation/UniversalRemotes.md @@ -25,6 +25,13 @@ Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [audio player universal remote file](/assets/resources/infrared/assets/audio.ir). +## Projectors + +Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. +To save time, please make sure every recording has been named accordingly. +In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. + + ## Air conditioners Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. From db1a8f8014ebff5243ac624d0c7c62614e3fb428 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:47:39 +0400 Subject: [PATCH 44/75] [FL-3099] SubGhz: add protocol KingGates Stylo4k (#2368) * [FL-3099] SubGhz: add protocol KingGates Stylo4k * SubGhz: add unit test file * f7: api: reverted symbols Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 11 +- assets/resources/subghz/assets/keeloq_mfcodes | 105 +++--- .../subghz/kinggates_stylo4k_raw.sub | 11 + assets/unit_tests/subghz/test_random_raw.sub | 6 + lib/subghz/protocols/kinggates_stylo_4k.c | 336 ++++++++++++++++++ lib/subghz/protocols/kinggates_stylo_4k.h | 74 ++++ lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + 8 files changed, 492 insertions(+), 53 deletions(-) create mode 100644 assets/unit_tests/subghz/kinggates_stylo4k_raw.sub create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.c create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 97629efea..c7e9c96f1 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -14,7 +14,7 @@ #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 317 +#define TEST_RANDOM_COUNT_PARSE 329 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -637,6 +637,14 @@ MU_TEST(subghz_decoder_nice_one_test) { "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); } +MU_TEST(subghz_decoder_kinggates_stylo4k_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"), + SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME), + "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -837,6 +845,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); MU_RUN_TEST(subghz_decoder_nice_one_test); + MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index 1b27bfb01..3eedc564f 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,55 +1,56 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: AA FF DE 54 A1 BB F1 21 83 46 FE 2A 1E B7 3D 33 -95B8CD65BBAC95EACE67CA94F679B82877A921396D461ECB479722F8A369454A -61065C41297B9FF8F8168814F49A03D1FE7B4CB79DFFCBBF0402AAA6A2211E84 -A1557AC139188FF105D1081A4B688C5CA440FB5DA7F40901B541120AD08A544F -AF0A6056D7F0D97DAD6C16C4E63204E4B3B1C5A20AC82B983B516F4F718EE29F -6861BFAE46A1AADB1DB2D6DFAA7E39D21D5B3E46A41BD50F4F2828879EB328EF0A406F2B9C79A031AB361257E6D69756 -0DDB3DAC53678541981CC46C22CED245CBA314C9BBE1BA9383B8505B75AC5E40 -99AB5D9404934F2D257ED04D9F8CCEE06D00F38157B121AFD63101E4E5C08268 -5114A6C42B342C7D933A76F9052FF963C2047E85EA524497C21B4C35C38EF6E7 -88CA2A1907D94B972FF93DBB9B88CB576F3E1BB0FE8F85A5B2CCA7D44B00374D -349C4153FE7CA8AE044E9F75F77D9694304474CE3F127CF968662B5F78A7F421 -62AA02E20CA7E691EFC0B55CA41C9BDF889FB23868289284241CD31AA1A0E499AE2A770B6B5AB3170CDCCDB8A246D36C -97901B5EB76228ADF8E5073F1BAB1502878DEFF1C4EBF12A43D105556CB7E80F947A8BD7831666BD838C57CDF64A6F3F -B05959D210B500943A93BDFAF783D9DB215FC84503B152EAFBCFB5B6237E3888 -B393DE4489BCAFD5DB80592A12E329E18913E185D2042580048029A8C4C3A257 -B4B30492A5F0C3C763E2F43C02D1451A5B9CFB468CFE62BE85B1F56FF49DAB9A -CE5D57C0EE3D717FC717EB725970A9F25D211546EE7AC5C237950CEA323D85D4 -4E9028944813FD40A17AF6DF5A97E76179B48EE79265BBD38B07E3A270587A813DADB51B3367479AC5644F754B5613F8 -3B3C3000B9D1361711ECE3DB77C90A059576F738CB167679DA36DD3D128B27A1 -997023148148DE7B9CBA47D3FD48DEF73AA1715FF4BC1E7A1DBA6D52A0DCB2C0 -C8428D18E69FB92486434FCE470F1FF37D40507F27D824679C132A70D516530367277F02DDB5C464D03450FF6B425A24 -3701200DF5DA7235971FD95844056E74C7D61A8EB12A8772E04F52037C63D50B6229A7F905F3E6F84C565FCC7632870C -BB392A464CDC0D5D923AA9EF8ECC3C6F020D0AD82165462DF0DE7C5025AAAAAC -999C82209B30638506E5D708471676D2CBB4A432E5AF86ABD61179111EDAE636 -FDE2A452A6B47261338117EC20FC57731DA492562ECD21BBC61F098A5442CF20 -D923BABB5C4DFB48E3F763898B2796C7830D3EE9A91DF904AC2223A0F4736507 -0987DDAC695DD5E4607048DF1D4EF96599E17ED52F41785E676AA048AB7213FE -26CB3E6CFA10338A8DDD99BFF6957C53DEF435CB0FF977B71B5164ADFE11292A -097908FD07A0A093CA80E6FF59524707C1A11169D0CB6F8E4967D8DAA725FE7A -8C629E70A5CC6FCB039DFA1A6AC58CB7B7E92C85BDA66266AB49E6B1285FC7A6 -39A2052350CD446EDC1B9AD0C2DD51C78B2E5F3A76AAD0EC200F74B40ACD4AC5 -A1685CF8C4A5401F2CA0C8172CB5B4B5726C61CE68A72AE834B0A472CEB2F3DE -1F5ED5793DB381D1B501BA8A4DF3E74FB11FC1A922DDC8AE62E5BA8934C37EA8 -D80EF661BF36E2F6C179E253CE5BC3732684ACBC7C65E526A628442A2EBF8FAA -7785BF721F21E19A8CFBFBBB56BD76B96A4E8EF9F8A2344009B14AB385909598F834A5533B648DA7D62BD6D4314A43A5 -C8F6F943DE615B5827569B283577344C0455B3279C73634FC4E0E9A8088DF633 -FB4F4C786FC51BBDA679A212B4A05EF120AC62F7EBFFE8263BD50A4D9BC9C6E0 -16EEC35CF69BA86DB3BE999CDF9B39F5736F3727B2AA2C5AB9141A48F176D831 -AD1AD6DE813E7710DA3AF546D4F9EA085831E6B3FA17B64F1B8765F48134EA54 -345D743BC35B4A8614632ADD11E809C0D1E6C78F9469256B9A738DA0B648B2B8 -7C876CECAC839EBB4609C3996966C3EC454F51C8ABCC51097E405370C4B6F086 -0F857C031FD3047607647148C534F969567F207FF1691D8D06DCDF4C2514695D -EC0630EDC82241C1952F49B6B1B0C1A954A7DDD6BDB1326ACC54AD449D1BF985 -286EF9F7FD0D09F2604CCE867C52144CD0C4773A3D8183066C61B8BF9860AE7C -EA55424097A08722A66966E3177E09DE91AC65175E5C68CB47B6153E6585DF85 -D54FCDF9EA4BD1FE4F316DB6D5CE4A2675F2D0144772865EDC781FBA7DFD23E4 -7A2F5C5CA9F97FE9527BAA760E64B930C407A27DE036476737E6BDD9422F4056A5F1F414F12F0982109FD7C30E8CC1CB -06BAD9B4EEEEB1BCF8C97672D271534FE84D772282EE9642698788D3842D7641 -101C1B2DBD963E23777294C22E553D145D5B40838F91355CA86D571A0CEFF68F -1B148C2B502B3E0A5BD40858E019C513DD4CCAF2A114CBB29C59BFB018079285 -8DF4D07EC20FF873EA989ACEF4AF96E9787FE6E0F71965858B4186C3AF302A31 -2317DC8C098CD60F3467B3644A19CCE887339708820CD37F6F5277D6648F837512F70CE90E23D7339CDDE002BD8D83DB +IV: 41 84 34 43 84 1D 43 04 45 44 34 38 41 24 3E 74 +8C5AEF725F0620DB3952B40BB8E76A815BCEE1D1B92F7E8E8E63D1894F1C7FD0 +1DFF1D6A322D6B3D8AD7C594A02462AADE723D417B9233585526982F08187DAA +0A9184F15D4A5589DDDA6422063BACD58580661CFE60EE600D87F73F0CB5013E +6E56802DAA049C3DFDEDC90432A0E694A172C369EBECD136F4C911B979AA098D +A659716B51053604059F7FC3651D6A153F5EAB1852F95B20C44C41A7889A0DE91A078B63E3C311280C4315F0A3C8BA1F +A315170EDC51627157725D9A96490DB75EBF8232957FBA313C03B2BA2884EA85 +DEAB3C2C2E2DC76FE45AEBAC7EBFB478CECCD970A63B8DE2024FBFDCCBD1B26E +7BBFC36CBA77468B4624C6B685610877D53985C985DAD8EFE47527EB7C7260CD +879EE18B314ED4F3F548B41176099176FB97F4F1A062481C935B2DDFBCE2FE4D +493372D7D47A96A66305DFDC8A915EB651620881AE1D603B7E9605E004C04CA9 +F80AAA4C447F8E8C0B039DDAECF9126119C32FF164118780BE268E326A8CBF8010DE2EBF94033CEAC39815D6A8958CF4 +41C1393A039E665F6A53A5E5D1C105008BD14D9755751545A667860C39B2E39AA47306E76E2BA7DDDAA2A286FDB58D23 +34853A4CDE42CB80045E641AB4800C86C1CF6907EAAFA399156CCC727A008584 +D0783A34BD6A36D31BFF5F70FA1116CAE48EF02716D80481AE406DABB3C3400E +0BB3582605434CF2A5D74A27773B88DA331B6033C444837E9F7A155897258B03 +E4E71F3EB290B9436FFF0FDADA468BE37D964E89BE8D9971A14776F821822769 +744AA59D129C892120B5DAB81C8A3D573B0AD80EF0708C1B9ECF13DA60CECA07DC0591A08611DB4D3A8B7C70994D5DEF +716F9F8D5D2C697BC4183EFCC97E52D08ECA07B613A0F389C801F65803DFF4A4 +560262DA8489D2C18C8D97E314DC663403AFE4DE9DCB6D453087D2BFBD36532D +9E31F7152C50B6940EE3E3894C98F75702C7897F702B48E5C9B54B6E25083641AD2E521267505066C7E5BAB7F6CF1433 +6630EDA18A6E58BD395792CCC656DD10CD9C5DD2B1949FE677122FA39A53C724E79C0D0752A3A39A03407BBA2282185E +00D15A06F5DD82A4B926B78809CC4D129AAFA9A92B0A81568F255D15697FE0FD +29FF9A4F5346ABEE8FEDE034988F87FCD29EA747735898F1E7207EF74FAB71A8 +C0E8EB6AE6F77EE38DF2AB1B7742E34ED5236F3D8E964845E66762A4675AA21F +00FC4C459DC4CE92B62D0AC2546F9FBBE0893F84D2AF0A20ED462A5EAE63DE3B +E92EF482A40CEEFC8339BBB713BBC452A266A09B2645EDEB12716544B2DB9B09 +D7D9C5C757831BCE2FF1DB25A080D77769FB36A1F3F48F4361418A0A45609280 +C19246F52AE1EE5CE968CED52F642D9CD78B020029632FE83C49C657D23ED075 +FEE3C05432FB3860D5D28562323F5D1B053B8F3ADCD416BD0C4645F6F4D43DCF +D780A4AADD0205E0BACDCC9AF46ED259E0946C5DA888C341BFE96E09A87CCCFA +CE3C13CFA08E532B637FDB707E29548D57EE92EAEF6516C3D67E9D36FCD59CF9 +5E88CE71258CB0D91631FEB41C9A2F47AE0FF4810A9A1EDF3F308BBDE6944D5E +1531F4107FC64810BA5DB5E46C7B9AD61531AF5430E137B7688109FBC06B6221 +68050A39C0B302E0B713FAAC5F829C79AB30E18B1D982A94005DBAC7CCFB95379A619C0B9F7409C44D19FF2C5E8E4546 +3F73E8BA22C602280496EF8E88E2CAA9EC442E3B3083B684942DBF9CB5121241 +FA1FCD7C9182FAE8FFF4E88433AE68F66076B3BDFF8AD0BF5CEA43870082E9BE +DFF7DD2678C03401656B093BF7AC7E033F15FD0F30188E48A62045740B423699 +371BCFF653E7811D99C048A1A39921AAA563E06AC86CB3D2F392C2C955A1ABD0 +F4F1766DEAEDE934478208B9EB3050326D9FFCC001C73EEE93407D8B12CD49E4 +A241C9FC62DDF67D645936245FAFFE2A42C86151F484B7BCE5410E8F36FC87901D3AC4E40334E08FFFC2AD676E490D94 +3566A94A9C0479E0C4387D9137375ADF2C921504364F3903F198D6757CDFD21B +7274E1B5A6445FDC29C355D550E981C17F349BC4A14251B3B51BC96FC334FBCA +04EEA5EDD9B3BC3E0638E53A5561DC8BF761D615A64D435BD31A94AF2650159E +B84818CC1695FE8B731CD653D0679D1AAA0578C0B06AD1E3510785B2DE20841C +4121343D6B79E38C06DD038D770D76D10336AFF47ED0D0DCDDD6B0FEA4DAE67C +75E49C839CCD7019D9CE90AC364F488468B2AB01E387A8BEF8815915925166A6 +CFAA9F4717568C1EC7B96E0D71D260B828A70484E1D9CA7C99A50D10704F8BBBAE62EE98C9FBDFF06F357F1C1E2F2677 +41E4D250B92BC57442B91DE2015C41226531CF9A8D77B83AFC8E4F3183DB11DE +45EA8BD854D7F044FB249C16F08A0C24FF117D54BC20A4CC667B3DAD09EAC4F9 +F455CA0BB8B496C301406DE4FB52C9B0F64645776803BC2935A2F38675318BE2 +22FF72A5D2E1A2EBFB6C55FFD0A3CEA0474CCBD13462D63229C9708276E87D3F +8470F9A300170F226C0216C07AA829591CBD4CE34AA918EAE49363BDE86CC77EEEBEEA84A097488D35B92F773F5DBB4C diff --git a/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub new file mode 100644 index 000000000..49b190002 --- /dev/null +++ b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 12b08d48d..949a44458 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -190,3 +190,9 @@ RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 4 RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..2c8de0d2d --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,336 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + uint64_t data, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->data, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->data, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..c9f1cf380 --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 3904c1811..050904eec 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -41,6 +41,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_linear_delta3, &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index cd9b7c6ed..522931d22 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -41,5 +41,6 @@ #include "holtek_ht12x.h" #include "dooya.h" #include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From 8f2f2d810ab4112605adee7f605613ff3c151f10 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 8 Feb 2023 10:06:42 -0800 Subject: [PATCH 45/75] Move CSN space to revent overflow (#2232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../plugins/picopass/scenes/picopass_scene_read_card_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index bb170ac45..0d1cc78c3 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -33,7 +33,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { uint8_t csn[PICOPASS_BLOCK_LEN]; memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, " %02X", csn[i]); + furi_string_cat_printf(csn_str, "%02X ", csn[i]); } // Neither of these are valid. Indicates the block was all 0x00 or all 0xff From a00508763652a424049b337b26386adc2ad4248d Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 8 Feb 2023 22:16:05 +0400 Subject: [PATCH 46/75] fbt: building fap_dist for compact gh build; accessor: fixed for latest ibutton changes (#2377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: building fap_dist as default target; accessor: fixed for latest ibutton changes * fbt: not building fap_dist as default target; github: doing fap_dist for compact builds Co-authored-by: あく --- .github/workflows/build.yml | 2 +- applications/debug/accessor/accessor_app.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6c9219c2..be9817684 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,5 +188,5 @@ jobs: set -e for TARGET in ${TARGETS}; do TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package done diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 9d3708ebe..337437d0e 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -34,7 +34,7 @@ void AccessorApp::run(void) { AccessorApp::AccessorApp() : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); - onewire_host = onewire_host_alloc(); + onewire_host = onewire_host_alloc(&ibutton_gpio); furi_hal_power_enable_otg(); } From 892adcc6956cf929457e42c6a2a5a167ff78b0af Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:46:33 -0500 Subject: [PATCH 47/75] added pager modulation, removed duplicate modulations, and cleaned up duplicate setting file --- applications/main/subghz/subghz.c | 27 +++++++---- applications/main/unirfremix/unirfremix_app.c | 2 +- applications/plugins/protoview/app.c | 2 +- assets/resources/subghz/assets/setting_user | 45 ------------------- .../resources/subghz/assets/setting_user.txt | 21 --------- 5 files changed, 21 insertions(+), 76 deletions(-) delete mode 100644 assets/resources/subghz/assets/setting_user diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index c39c35679..d95133da7 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -183,7 +183,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { //init setting subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user.txt")); // Custom Presets load without using config file @@ -208,26 +208,37 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset2); - // # HND - FM presets + // Pagers FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); flipper_format_write_string_cstr( - temp_fm_preset3, + temp_fm_preset2, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3); flipper_format_free(temp_fm_preset3); + // # HND - FM presets FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_1", temp_fm_preset4); - flipper_format_free(temp_fm_preset4); + flipper_format_free(temp_fm_preset3); + + FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset5, + (const char*)"Custom_preset_data", + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + flipper_format_rewind(temp_fm_preset5); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_2", temp_fm_preset5); + + flipper_format_free(temp_fm_preset5); // custom presets loading - end diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/unirfremix/unirfremix_app.c index 2c0b68ae7..2b12a12b2 100644 --- a/applications/main/unirfremix/unirfremix_app.c +++ b/applications/main/unirfremix/unirfremix_app.c @@ -706,7 +706,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { void unirfremix_subghz_alloc(UniRFRemix* app) { // load subghz presets app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); // load mfcodes app->environment = subghz_environment_alloc(); diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index d060e2242..4765da9e3 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -143,7 +143,7 @@ ProtoViewApp* protoview_app_alloc() { //init setting app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); // GUI app->gui = furi_record_open(RECORD_GUI); diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user deleted file mode 100644 index d10392341..000000000 --- a/assets/resources/subghz/assets/setting_user +++ /dev/null @@ -1,45 +0,0 @@ -# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user -Filetype: Flipper SubGhz Setting File -Version: 1 - -# Add Standard frequencies for your region -Add_standard_frequencies: true - -# Default Frequency: used as default for "Read" and "Read Raw" -Default_frequency: 433920000 - -# Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" -Frequency: 300000000 -Frequency: 310000000 -Frequency: 320000000 - -# Frequencies used for hopping mode (keep this list small or flipper will miss signal) -Hopper_frequency: 300000000 -Hopper_frequency: 310000000 -Hopper_frequency: 310000000 - -# Custom preset -# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register - -Custom_preset_name: AM_1 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -Custom_preset_name: AM_2 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -Custom_preset_name: FM95 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -# Honda Presets -Custom_preset_name: Honda1 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -Custom_preset_name: Honda2 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 \ No newline at end of file diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt index 06a699a6c..173511f40 100644 --- a/assets/resources/subghz/assets/setting_user.txt +++ b/assets/resources/subghz/assets/setting_user.txt @@ -78,24 +78,3 @@ Hopper_frequency: 868350000 #Custom_preset_name: AM_2 #Custom_preset_module: CC1101 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -# Custom presets added in Unleashed FW -# -- Some presets from forum.flipperzero.one -- - -#2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping -Custom_preset_name: FM15k -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0 - -# -- Other presets -- - -# Honda Presets -Custom_preset_name: Honda1 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -Custom_preset_name: Honda2 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 From 65ed2ed27756ac051ddb07e9b3d3092d6b895ac3 Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:54:06 -0500 Subject: [PATCH 48/75] added pager bruteforce for lrs pagers thanks jimi! --- .../subghz/Misc/Pager_Bruteforce.sub | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 assets/resources/subghz/Misc/Pager_Bruteforce.sub diff --git a/assets/resources/subghz/Misc/Pager_Bruteforce.sub b/assets/resources/subghz/Misc/Pager_Bruteforce.sub new file mode 100644 index 000000000..27e8a5430 --- /dev/null +++ b/assets/resources/subghz/Misc/Pager_Bruteforce.sub @@ -0,0 +1,108 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 467750000 +Preset: FuriHalSubGhzPresetCustom +Custom_preset_module: CC1101 +Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00 +Protocol: RAW +RAW_Data: 2950 -8252 551 -184 549 -7528 1285 -554 369 -187658 219 -138090 28885 -1206 4003 -2610 3601 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3004 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3393 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 3389 -3002 1801 -1400 3401 -3008 3397 -2994 3401 -1398 1791 -4422 621 -2182 1689 -2756 3511 -3098 3253 -3022 3413 -3010 3403 -3002 3407 -3004 3399 -3006 3393 -2994 3401 -3002 3395 -3002 3393 -3002 3395 -3002 3405 -1400 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3407 -3004 3393 -1398 1791 -3000 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 3397 -3006 1805 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -2998 1805 -1396 3391 -3002 3393 -1402 1799 -3002 1797 -1398 3801 -2190 219 -1768 1767 -2596 3795 -2728 3493 -2872 3443 -3022 3417 -3002 3407 -3006 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1795 -1398 1801 -1400 3407 -3006 3393 -1398 1791 -2998 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 3395 -3002 1795 -1398 3401 -3002 3403 -1396 1791 -3000 3401 -7590 1961 -2458 3749 -2762 3575 -2990 3395 -2998 3393 -2994 3407 -2998 3393 -2994 3407 -2998 3397 -2998 3393 -3002 3395 -3002 3407 -2996 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3393 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -2994 1799 -1402 3407 -2996 3395 -1400 1795 -1394 1797 -3002 3603 -200 1143 -188 1487 -1226 1827 -2996 3419 -3010 3401 -3002 3401 -3004 3401 -2994 3401 -3002 3399 -3006 3393 -2994 3401 -2996 3401 -3002 3399 -2996 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -2992 3401 -3002 1801 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -3002 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 3407 -2996 1797 -1394 3397 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -6446 2633 -2932 3363 -3180 +RAW_Data: 3189 -3190 3395 -2994 3393 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -3002 3395 -3002 3405 -2998 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3393 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -3002 3399 -3004 1795 -1396 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -2996 1799 -1402 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1799 -1402 4405 -2974 1981 -3218 3193 -3160 3241 -3164 3313 -3118 3309 -3084 3243 -3026 3413 -3002 3407 -3010 3409 -3002 3399 -2996 3391 -2994 3401 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -2996 3401 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -2998 1795 -1394 3389 -1402 1801 -3002 1799 -1402 1801 -1400 3401 -3596 385 -560 373 -2330 8379 -2512 3729 -2750 3565 -2984 3385 -2990 3395 -3002 3393 -3002 3393 -3004 3401 -2998 3405 -2994 3395 -2994 3401 -3002 3403 -1400 1795 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1799 -1402 1801 -1396 1791 -1398 3407 -3006 3393 -1402 1795 -2998 3403 -2994 1801 -1400 1801 -1396 1797 -1400 1801 -1402 3401 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 1799 -1402 3407 -1400 1789 -2990 1799 -1402 3401 -3002 3609 -3274 2973 -2884 3537 -2960 3375 -2986 3389 -2996 3393 -2994 3393 -3002 3403 -3002 3401 -2994 3407 -2998 3393 -2994 3401 -3004 3393 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -1400 1795 -2992 3401 -3002 1801 -1402 1795 -1398 1801 -1400 3401 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 1797 -1398 3401 -1396 1797 -3002 1801 -1400 3403 -1396 1791 -3800 205 -3344 203 -612 2395 -2560 3555 -2896 3655 -2830 3419 -3014 3407 -3004 3393 -3004 3401 -2994 3401 -2994 3395 -3002 3401 -3002 3395 -3002 3401 -2998 3397 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1400 1801 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 3407 -3006 1795 -1394 3389 -3002 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 3407 -2998 1795 -1394 3397 -1402 1799 -3002 3395 -3002 1795 -1398 4003 -4020 1655 -3022 3323 -3148 3163 -3176 3385 -2990 3393 -2996 3401 -3002 3401 -2994 +RAW_Data: 3395 -3002 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1799 -3004 3397 -3006 1795 -1394 1791 -1400 1799 -1402 3401 -3002 3401 -2996 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 3401 -3002 1801 -1400 3407 -1400 1789 -2990 3407 -3004 3395 -8162 1623 -2776 3591 -2998 3389 -3000 3397 -2994 3393 -3002 3395 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1799 -1398 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 1795 -1394 1797 -1402 3405 -3006 3393 -1402 1799 -2996 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -3002 1799 -1398 3389 -1402 1801 -3002 3401 -1396 1793 -2998 3403 -1030 647 -3066 1817 -2644 3739 -2768 3575 -2986 3389 -2994 3395 -3002 3393 -3002 3399 -3006 3393 -2994 3401 -3004 3393 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3407 -3004 3399 -1404 1793 -2986 3401 -3002 1797 -1394 1795 -1402 1801 -1400 3407 -1404 1795 -2992 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 +RAW_Data: 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3407 -3004 1795 -1394 3391 -1404 1801 -2994 3403 -1400 1795 -1394 1797 -6646 2743 -2920 3547 -2968 3181 -3190 3391 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3006 1799 -1394 1791 -1398 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3002 1795 -1398 1801 -1400 1801 -1400 3407 -1400 1791 -2996 3405 -2994 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 1799 -1402 3401 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 3595 -1062 1325 -884 2861 -2628 3677 -2784 3481 -2744 3683 -2912 3513 -2916 3333 -3156 3175 -3182 3191 -3190 3393 -2994 3403 -2994 3401 -3002 3393 -3004 3393 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3405 -2998 3393 -1402 1795 -2992 3401 -3002 1801 -1400 1795 -1398 1801 -1402 3401 -1396 1793 -1398 1799 -3004 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 3401 -3002 1801 -1396 3389 -1402 1801 -1400 1801 -3002 1801 -1396 3391 -6068 219 -1318 1767 -2830 3479 -2936 3489 -2910 3509 -2922 3341 -3162 3175 -3186 3391 -2994 3393 -2998 3407 -2994 3393 -3002 3393 -3004 3401 -3002 3401 -1398 1791 -1394 +RAW_Data: 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3407 -1400 1789 -2990 3401 -3004 1799 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1797 -1398 3401 -1396 1797 -1402 1799 -3002 3403 -2994 4221 -2588 2113 -3134 3299 -3086 3259 -3026 3409 -3002 3407 -3006 3397 -3006 3393 -3002 3395 -3002 3393 -3002 3407 -2998 3393 -3002 3393 -3004 3401 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1396 1797 -1402 3401 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 3401 -3006 1795 -1394 3391 -1400 1801 -1400 1801 -3002 3393 -1402 1801 -3802 207 -2344 219 -1326 1669 -2836 3513 -3130 3147 -3176 3181 -3186 3391 -2994 3393 -3002 3395 -3002 3401 -2994 3401 -3004 3393 -3002 3399 -3004 3395 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3403 -1396 1791 -3000 3407 -3004 1797 -1394 1791 -1398 3401 -3002 1801 -1400 1797 -1394 3397 -3002 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 3401 -2998 1805 -1396 3391 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 3607 -4140 2069 -2820 3359 -2938 3533 -2914 3513 -2916 3337 -3160 3173 -3184 3393 -2998 3389 -2996 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -3002 1799 -1398 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -3002 1799 -1402 1801 -1396 3391 -3002 1799 -1402 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 3399 -3002 1799 -1402 3401 -1402 1795 -1394 1797 -1400 1801 -3002 3401 -7030 2161 -3078 3391 -2994 3389 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3006 1799 -1394 1791 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -2994 3403 -3002 1799 -1398 1791 -1398 3403 -3002 1799 -1402 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 1799 -1402 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -3002 3795 -3692 2201 -2812 3459 -2876 3485 -3104 3309 -2934 3359 -3176 3181 -3190 3395 -2994 3393 -3006 3399 -2994 3401 -2994 3407 -3006 3389 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -3002 1801 -1398 1791 -1398 +RAW_Data: 1801 -1400 3401 -3004 3393 -1400 1801 -3002 3399 -3006 1795 -1394 1797 -1400 3401 -2994 3403 -2994 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3394 399 -3496 2327 -2200 3995 -2554 3791 -2544 3685 -2710 3691 -2920 3333 -3156 3373 -2984 3391 -2996 3389 -2996 3401 -2994 3401 -2996 3405 -3002 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3002 3395 -1400 1801 -3002 3401 -2996 1795 -1398 1799 -1402 3401 -2994 3403 -3002 3393 -3002 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 3403 -3002 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3593 -4356 1945 -2932 3435 -3014 3415 -3004 3401 -3004 3393 -3002 3401 -2994 3407 -3006 3393 -2994 3395 -3002 3401 -3002 3399 -3004 3395 -2994 3407 -1400 1789 -1392 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3407 -3004 3395 -1396 1793 -2998 3401 -3004 1799 -1398 1791 -1398 3401 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3407 -3006 1795 -1394 1791 -1398 1801 -1400 1801 -1402 3401 -4768 389 -390 193 -2086 1793 -2908 3435 -3014 3409 -3002 3407 -3006 3401 -3002 3393 -2996 3401 -3002 3407 -2996 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3395 -1400 1797 -2998 3401 -3004 1799 -1398 1791 -1398 3401 -3002 3403 -1396 1797 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 3393 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3593 -1506 4053 -3028 3495 -2918 3323 -3148 3167 -3182 3185 -3192 3389 -2994 3401 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -3002 3395 -3002 3393 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 3393 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1398 3389 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 3397 -3002 3401 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -1402 1801 -5898 771 -378 2029 -2840 3559 -2776 3585 -2986 3395 -2994 3393 -3002 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -2998 3407 -2994 3401 -2994 3395 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1398 1795 -3002 3403 -2994 1799 -1402 +RAW_Data: 1795 -1398 3401 -1402 1801 -3002 1795 -1394 3403 -3004 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3401 -2994 1801 -1400 1795 -1400 3405 -3006 1795 -1394 3591 -3806 2201 -2824 3453 -2878 3493 -2908 3521 -2926 3349 -3164 3177 -3190 3387 -2990 3393 -2996 3401 -3006 3397 -2994 3403 -2994 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -3002 1795 -1398 1801 -1400 1795 -1398 3407 -3006 3393 -1398 1791 -2998 3403 -3002 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -2994 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -2996 3401 -3002 1795 -1394 1797 -1402 3401 -3002 3401 -5912 1929 -3004 3419 -3010 3411 -2996 3395 -2994 3401 -3002 3401 -2994 3407 -2998 3393 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3403 -2994 1801 -1400 1801 -1400 3395 -1400 1801 -2994 3401 -2922 1031 -1104 1767 -3090 3163 -3166 3183 -3186 3393 -2994 3395 -3002 3401 -2994 3403 -3002 3397 -3006 3393 -2996 3401 -2994 3401 -2994 3403 -3002 3399 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1400 1799 -1402 1799 -1402 3407 -2996 3395 -1400 1795 -2992 3401 -3002 1801 -1402 1799 -1398 3389 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3403 -2994 1799 -1398 1797 -1400 3407 -1400 1791 -1394 1797 -8408 1655 -3216 3289 -3110 3319 -3136 3159 -3176 3185 -3200 3197 -3194 3389 -2996 3393 -2994 3401 -3002 3403 -2994 3401 -2994 3403 -3002 3397 -1404 1797 -1394 1795 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1795 -1398 1801 -1402 3401 -1396 1793 -1398 1799 -3004 3405 -3006 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3393 -3002 3401 -2996 1799 -1402 3401 -3002 1795 -1394 1793 -1398 4403 -1594 579 -948 2021 -2944 3439 -3026 3417 -3002 3411 -3002 3401 -3002 3393 -3004 3401 -2994 3407 -2996 3399 -3006 3393 -2994 3395 -3002 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3401 -3004 3393 -1400 1801 -3002 3395 -3002 1799 -1402 1795 -1394 3397 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -3004 3397 -3006 1795 -1394 3391 -3002 1799 -1402 3401 -7960 1797 -3000 3435 -3014 3415 -3004 3407 -3006 3393 -2994 3407 -3002 3397 -2994 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3405 -1400 1793 -2990 3403 -3002 1799 -1402 1795 -1398 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 3407 -3006 3393 -2994 1801 -1396 3391 -3006 3405 -2994 3803 -4598 1425 -3262 3367 -2984 3185 -3190 3391 -2994 3405 -3006 3393 -2996 3401 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -3002 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 +RAW_Data: 1791 -1398 1801 -1400 3401 -3002 3403 -2994 1799 -1402 3401 -2994 3407 -1400 1791 -4212 203 -2976 1877 -3038 3175 -3164 3353 -2932 3519 -3106 3309 -3122 3147 -3168 3183 -3182 3389 -2994 3395 -3002 3393 -3002 3395 -3006 3397 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3403 -2998 3405 -1396 1793 -2998 3403 -3002 1799 -1398 3397 -3002 1797 -1398 1799 -1402 1799 -1402 3407 -2996 1797 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1396 3397 -804 201 -2984 1719 -3010 3487 -2908 3515 -3134 3147 -3168 3183 -3186 3389 -2998 3399 -2994 3393 -3002 3407 -2998 3393 -2994 3401 -3004 3401 -2994 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -2998 1805 -1398 1791 -1394 1797 -1400 3401 -3002 3403 -1400 1801 -2994 3401 -3002 1797 -1394 3397 -3002 1801 -1400 1801 -1402 3401 -2994 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -2994 1799 -1402 3393 -1402 1799 -3004 3393 -3202 803 -3626 2215 -2456 3759 -2776 3585 -2990 3391 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -3006 3397 -2994 3403 -2994 3401 -3002 3395 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3407 -3006 3393 -1398 1795 -3002 3403 -3002 1795 -1394 3397 -3002 1801 -1402 1799 -1398 3389 -1402 1799 -3002 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 +RAW_Data: 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -3002 1801 -1400 3395 -1400 1801 -1396 1797 -3002 4211 -4018 1715 -2938 3433 -3018 3411 -3010 3401 -3002 3401 -3002 3407 -2998 3393 -3002 3395 -3002 3393 -3002 3393 -3004 3401 -3002 3393 -3002 3395 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1400 3401 -2994 1801 -1402 3393 -3002 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 3403 -3002 3401 -3002 1795 -1394 3403 -1404 1795 -1394 1797 -1402 1799 -4574 383 -2458 2107 -3020 3263 -3042 3423 -3010 3405 -3006 3401 -3002 3407 -2998 3393 -2994 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -2994 3403 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3000 3405 -3006 1795 -1394 3391 -3002 1799 -1402 3401 -3002 3401 -2996 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 3403 -2994 3401 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 3603 -838 437 -2646 1921 -2808 +RAW_Data: 3533 -2914 3495 -2868 3443 -3022 3417 -3002 3407 -3010 3401 -2998 3393 -2996 3393 -3002 3401 -2994 3403 -3006 3397 -2994 3407 -1400 1789 -1392 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -3002 1795 -1398 3401 -3002 1797 -1398 3401 -1400 1797 -2994 1805 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -3002 3393 -3004 1795 -1398 1799 -1402 3393 -3804 421 -1492 221 -1726 1751 -3022 3331 -3148 3177 -3180 3185 -3194 3199 -3190 3393 -3002 3395 -3002 3393 -3002 3403 -2998 3405 -2998 3397 -2996 3393 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -3002 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3004 1799 -1402 3393 -3002 1801 -1396 3399 -1400 1801 -1400 1801 -2998 1805 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 3401 -3002 3395 -3006 3397 -2994 1801 -1402 3401 -2994 3801 -416 429 -2326 1741 -2894 3367 -2982 3385 -2992 3393 -3002 3401 -2994 3403 -2994 3401 -2994 3403 -3002 3405 -2998 3393 -2996 3401 -2994 3401 -3002 3395 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -3002 1801 -1400 3401 -2994 3403 -2998 1799 -1394 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 +RAW_Data: 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -3006 3397 -2996 1799 -1398 3389 -1402 1799 -7642 1653 -2952 3443 -3022 3413 -3014 3411 -3006 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3395 -2994 3401 -3002 3401 -2996 3401 -2994 3407 -1400 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3006 1801 -1392 1789 -1398 1801 -1400 3401 -3008 3405 -1396 1793 -2990 3403 -3006 1803 -1398 3389 -3002 3403 -2994 1799 -1402 3393 -3002 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -3004 3393 -3002 3401 -2994 3403 -3002 1795 -1394 4397 -596 1899 -1104 1803 -2886 3411 -3002 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2998 3405 -2994 3393 -3004 3393 -2994 3401 -3002 3403 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -3002 1801 -1398 3389 -3002 3401 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 3393 -3002 3401 -3004 3393 -3002 3393 -3002 3403 -3394 2509 -408 609 -1044 209 -420 1601 -3178 3319 -3096 3253 -3022 3419 -3014 3405 -3010 3403 -2996 3395 -2994 3401 -3002 3395 -2998 3405 -2994 3401 -3000 3397 -2994 3401 -1398 1791 -1398 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1396 3397 -3004 3401 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -3002 3403 -3002 3393 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 3397 -3002 3403 -2994 3401 -2994 3403 -1400 1795 -2992 4001 -212 211 -424 845 -2058 1877 -3244 3181 -3188 3389 -2994 3401 -2996 3401 -2994 3401 -2994 3403 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3407 -1400 1791 -2992 3401 -3002 1801 -1402 3401 -2994 3401 -1402 1799 -2996 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 3397 -3002 3403 -3006 3397 -2994 3401 -1402 1801 -1396 1791 -8006 2651 -1824 3861 -2834 3417 -3014 3407 -3002 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3395 -3006 3405 -2994 3393 -3004 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -2994 1801 -1402 1799 -1398 1791 -1398 3401 -3004 3401 -1400 1801 -2998 3405 -2996 1795 -1394 3397 -3006 3405 -1398 1791 -3000 3401 -2994 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3407 -3004 3395 -3002 3393 -1402 1795 -2992 1799 -1402 1799 -1402 3401 -4546 1981 -2786 3481 -2908 3497 -2922 3337 -3160 3175 -3186 3389 -2994 3399 -2994 3393 -3002 3403 -3002 3393 -2994 3401 -2996 3401 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -2998 1801 -1402 1799 -1398 1797 -1400 3401 -3002 3403 -1400 1801 -2994 3401 -2994 1801 -1402 3401 -2994 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 3401 -3002 3395 -3006 3405 -1398 1789 -2990 1799 -1402 3401 -4586 587 -746 551 -1102 2019 -2888 3497 -2864 3439 -3018 3413 -3002 3401 -3002 3403 -2994 3401 -2998 3407 -2994 3393 -3002 3393 -3004 3401 -2998 3397 -2998 3407 -1396 1791 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3393 -1402 1799 -3004 3393 -3002 1795 -1398 3401 -3004 3405 -1400 1793 -1394 1791 -1398 1801 -3002 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 3403 -2994 3401 -3006 3399 -1396 1791 -2992 3401 -3002 3603 -400 201 -1420 877 -1100 1971 -3176 3319 -2904 3511 -3132 3147 -3168 3181 -3186 3395 -2994 3393 -3002 3393 -3004 3401 -3002 3393 -2994 3403 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3393 -1402 1795 -2998 3403 -3002 1795 -1394 +RAW_Data: 3397 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3395 -3002 3401 -3002 3393 -1402 1795 -2992 3401 -1400 1801 -5262 401 -2280 2005 -2706 3647 -2842 3423 -3010 3405 -3006 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3002 3403 -2998 3405 -2998 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 3395 -3006 1805 -1396 3389 -1402 1801 -3002 1795 -1394 1797 -1400 3401 -3004 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -3002 3407 -1404 1793 -1392 1793 -2998 1801 -1400 3603 -2686 1231 -664 1505 -3148 3313 -3126 3149 -3166 3175 -3190 3193 -3192 3393 -2994 3401 -2996 3401 -3006 3397 -2998 3399 -2994 3401 -2994 3403 -3002 3401 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3006 3397 -1402 1795 -2996 3405 -2998 1805 -1396 3391 -1400 1801 -2994 1801 -1400 3403 -2994 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3407 -3006 3393 -1396 1793 -1398 1799 -3002 3403 -3602 861 -1102 1545 -442 1983 -3002 3277 -3092 3513 -3124 3147 -3168 3173 -3194 3195 -3190 3393 -3002 3395 -3006 3397 -2994 3395 -3002 3401 -2998 3405 -3000 3397 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1398 3401 -3006 3393 -1402 1795 -2992 3401 -3002 1801 -1400 3395 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -3002 3403 -1396 1793 -1398 1799 -1402 1799 -3004 3593 -610 3645 -2950 3285 -3122 3311 -3116 3323 -3138 3163 -3174 3187 -3194 3397 -2996 3393 -2994 3401 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1795 -1394 3399 -1400 1801 -3002 3401 -2994 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -3002 3393 -2994 3403 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -4212 1599 -1486 1653 -3012 3347 -3168 3181 -3188 3389 -2994 3395 -2994 3401 -3002 3401 -2996 3393 -3002 3401 -3002 3395 -3002 3399 -3004 3393 -2996 3401 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3399 -1404 1795 -2996 3405 -2994 1801 -1396 3391 -1400 1801 -3002 3401 -2996 3401 -3006 1799 -1392 1789 -1398 1801 -1402 1799 -1402 +RAW_Data: 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -2994 3401 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 1801 -1402 4617 -3624 1617 -3122 3183 -3190 3389 -2994 3403 -2998 3405 -2994 3393 -3004 3393 -3006 3397 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1400 1801 -3002 3393 -3004 1795 -1394 3397 -1402 1799 -3002 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 3401 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1402 3401 -3200 601 -3982 1955 -2718 3687 -2842 3425 -3016 3405 -3006 3401 -3002 3403 -2994 3401 -3002 3393 -2996 3401 -3006 3397 -2998 3407 -2994 3393 -3006 3399 -1396 1791 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1400 3403 -2998 3405 -1398 1791 -2992 3401 -3006 1799 -1394 3391 -1400 1801 -3002 3401 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 +RAW_Data: 1799 -1402 1801 -1400 1801 -1400 1795 -1394 3399 -3002 3401 -1402 1795 -2998 1801 -1402 1799 -1398 3389 -3002 3603 -608 209 -3446 2219 -2994 3443 -3026 3417 -3002 3407 -3008 3399 -2998 3405 -2994 3395 -2998 3405 -2994 3395 -3002 3405 -2998 3393 -3002 3395 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -1396 1797 -2994 3401 -3002 1801 -1398 3393 -1406 1799 -1402 1795 -3000 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 3389 -3002 3403 -1400 1801 -3002 1795 -1394 1797 -1400 3403 -1400 1801 -5432 663 -1710 1925 -2726 3617 -3010 3419 -3006 3401 -3002 3401 -2998 3407 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -2994 3401 -3002 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -3002 1801 -1400 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1795 -2992 3401 -3002 1801 -1400 3401 -1398 1791 -1398 1801 -3002 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 1801 -1402 3393 -2994 1801 -1400 3603 -3858 2219 -3186 3253 -3036 3417 -3006 3405 -3002 3403 -2998 3405 -2994 3401 -2996 3401 -2998 3405 -2994 3395 -2994 3401 -3002 3395 -3002 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2998 1799 -1394 1797 -1402 1799 -1402 3401 -2998 3405 -1398 1791 -2992 3401 -3002 1801 -1400 3403 -1400 1795 -1396 1795 -3002 3403 -3002 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 3405 -3002 3397 -1398 1791 -2992 1801 -1400 3401 -3002 3395 -6072 1197 -792 1865 -2724 3513 -2896 3455 -3034 3419 -3002 3401 -3002 3401 -3004 3401 -2994 3401 -2998 3407 -2994 3401 -2994 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3401 -3000 3405 -1396 1793 -2990 3407 -3006 1795 -1394 3397 -1402 1799 -1402 1801 -3002 3393 -1400 1797 -2998 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 3401 -3004 3393 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 4189 -3552 1729 -2986 3429 -3022 3419 -3006 3393 -3002 3399 -3004 3395 -2994 3401 -3002 3395 -3002 3393 -3006 3397 -3000 3405 -2994 3401 -2994 3395 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 3395 -1400 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 3393 -1402 1799 -3002 1797 -1394 +RAW_Data: 3397 -1402 1799 -1402 1799 -7326 2401 -2060 4155 -2504 3723 -2690 3661 -2838 3421 -3010 3411 -3002 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3401 -2996 3393 -2994 3401 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -3004 1799 -1398 1795 -1402 1801 -1400 3401 -3004 3393 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -1402 1799 -1402 1799 -1398 1791 -3000 3401 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3004 1799 -1398 1791 -1398 3601 -4120 1733 -2886 3559 -2960 3351 -3094 3255 -3044 3421 -3010 3401 -3006 3407 -3002 3393 -3002 3393 -3004 3401 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2998 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3403 -1400 1795 -2992 3401 -3002 1801 -1400 3403 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -3002 1801 -1396 3399 -5588 365 -1116 2145 -3134 3189 -3190 3395 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3393 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1396 3391 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 +RAW_Data: 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 3401 -3002 3393 -1402 1795 -3000 3401 -3002 3393 -3004 3593 -3932 2743 -2594 3595 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -2994 3403 -2998 3405 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -2998 3401 -3004 3401 -1396 1793 -7038 2817 -3032 3167 -3182 3385 -2996 3393 -2994 3401 -3002 3399 -3006 3393 -2994 3403 -2994 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -1404 1795 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1795 -3002 3403 -2994 3401 -3002 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2990 3403 -1400 1801 -3002 1801 -1396 3591 -4298 1767 -2654 3559 -2912 3513 -2914 3491 -2868 3443 -3022 3417 -3002 3407 -3006 3393 -3002 +RAW_Data: 3393 -3002 3403 -2994 3407 -3004 3395 -2994 3399 -1404 1795 -1394 1797 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1795 -1398 3401 -3006 3399 -1400 1795 -2992 3401 -3002 3403 -2994 1799 -1402 1801 -1396 1791 -1398 3403 -3002 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -3002 3395 -1396 1797 -3002 3407 -1404 1795 -2992 3401 -3400 1713 -1100 3677 -3172 3377 -2982 3187 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3399 -3004 3393 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 1791 -1398 3401 -3002 3403 -1400 1795 -2992 3401 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1795 -3000 3801 -1120 181 -920 553 -1654 1653 -3052 3481 -2942 3489 -2908 3299 -3120 3335 -3164 3175 -3182 3389 -2996 3393 -3002 3401 -2996 3393 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 3403 -3006 1599 -1592 1789 -1398 3401 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1797 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3402 1057 -3748 1469 -3298 3149 -3174 3175 -3190 3393 -2994 3395 -3002 3393 -3002 3403 -3002 3393 -3002 3399 -3004 3395 -2994 3401 -2994 3403 -2994 3401 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3395 -1400 1801 -3002 3401 -2994 3403 -2994 1799 -1402 1801 -1400 3393 -3004 3393 -3002 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3601 -3262 435 -874 1833 -3302 3235 -3018 3405 -3002 3407 -2998 3393 -3002 3393 -3002 3399 -3006 3393 -2994 3403 -3002 3393 -3002 3393 -3008 3397 -3002 3393 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 1799 -1402 1801 -1396 3403 -3004 3395 -1396 1793 -2998 3401 -3004 3401 -2994 1801 -1400 1795 -1394 3399 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 3401 -2994 3403 -1396 1797 -1400 1801 -3002 1795 -1396 1795 -1402 3401 -7588 1923 -3068 3393 -2994 3403 -2994 3407 -3004 3399 -2998 3393 -2994 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3403 -2998 3409 -1400 1789 -1394 1795 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -2998 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3403 -1400 1801 -2994 3407 -2996 3395 -3002 1795 -1394 1797 -1400 3401 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -3002 3401 -1398 1791 -1398 1801 -3002 1801 -1400 3401 -2996 3607 -1728 833 -1396 2303 -2880 3581 -2986 3391 -2994 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3393 -2994 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3407 -1400 1791 -2992 3401 -3002 3407 -2998 1795 -1394 3389 -3002 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3407 -2996 3395 -1400 1795 -1398 1801 -3002 1801 -1400 3395 -1400 1801 -7606 1737 -3220 3235 -3022 3417 -3002 3407 -3004 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3393 -3004 3401 -2994 3393 -3002 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3407 -1400 1789 -2990 3401 -3002 3403 -3002 1799 -1398 3389 -3002 1801 -1402 3401 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1402 1795 -1394 1797 -3002 3401 -3002 1801 -1398 4195 -1886 1105 -442 1765 -3046 3269 -3160 3341 -3122 3291 -3112 3319 -3140 3163 -3176 3185 -3190 3391 -2994 3393 -2998 3411 -2998 3393 -3002 3393 -3004 3393 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3401 -1402 1795 -2992 3401 -3002 3401 -3004 1795 -1394 3397 -3002 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -3002 3407 -1400 1791 -1394 1797 -3002 3401 -3002 3395 -5516 1583 -3012 3489 -2910 3305 -3120 3337 -3164 3173 -3186 3191 -3190 3399 -3004 3395 -2994 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -3002 1801 -1400 1795 -1394 1797 -1402 3405 -3006 3393 -1402 1795 -2992 3401 -3002 3401 -3002 1797 -1394 3397 -3002 3407 -1404 1795 -2992 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 3401 -3002 3401 -1398 1791 -1398 1801 -3002 3401 -1398 1795 -3002 3595 -3624 415 -206 2483 -2414 3585 -3002 3401 -2994 3401 -3004 3393 -2994 3401 -3002 3403 -2998 3411 -2996 3395 -2994 3393 -3002 3393 -3004 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 3407 -3004 3399 -1400 1789 -2990 3403 -3002 3401 -3002 1795 -1394 3403 -1404 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1795 -1394 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -1402 1799 -2994 3403 -1400 1795 -1398 1801 -8014 1843 -2840 3389 -2994 3403 -2994 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3401 -2998 3399 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3405 -3006 3393 -1398 1791 -3000 3405 -3006 3393 -2994 1801 -1398 3389 -1402 1799 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 3395 -4724 1853 -2864 3547 -2976 3181 -3186 3391 -2994 3393 -2994 3407 -3006 3399 -2996 3399 -2998 3393 -3002 3399 -3006 3393 -2994 3401 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3004 3393 -3002 3401 -2994 1801 -1402 3393 -1400 1797 -1398 1799 -3002 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 3397 -5708 365 -1470 9113 -1824 3823 -2614 3609 -3002 3403 -3002 3401 -3002 3401 -2994 3403 -3002 3393 -3002 3399 -3006 3393 -2994 3401 -3004 3393 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -3006 3397 -2996 1799 -1398 3397 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -3002 3407 -1404 1795 -1394 1791 -1398 1801 -3002 3401 -2996 3601 -1690 629 -2154 1905 -2832 3651 -2830 3423 -3006 3401 -3002 3401 -3002 3395 -3002 3401 -3002 3395 -3002 3405 -2998 3393 -2996 3401 -3002 3407 -2996 3395 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1795 -1394 1797 -1402 3401 -3002 3401 -1402 1795 -2996 3409 -2998 3393 -2994 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 3401 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -3002 3401 -1402 1795 -3600 639 -3748 1839 -2644 3729 -2756 3567 -2982 3391 -2990 3393 -2994 3403 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -3002 3403 -2994 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3401 -3004 3397 -1404 1797 -2990 3403 -3002 3405 -2998 3393 -2996 1799 -1398 1795 -1402 3401 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 +RAW_Data: 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -3000 1801 -1400 3801 -4494 1987 -2568 3839 -2822 3413 -3002 3403 -3002 3401 -3002 3401 -3002 3407 -3010 3393 -2994 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3004 3393 -3002 3401 -3002 1797 -1394 3389 -3002 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -3002 3405 -8042 1841 -2856 3557 -2932 3531 -2910 3509 -2922 3345 -3160 3173 -3188 3395 -2996 3391 -2994 3401 -3002 3399 -2998 3393 -3002 3401 -2994 3395 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -3002 3403 -2994 1795 -1398 3407 -1404 1795 -2992 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 3395 -3002 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -2994 3603 -4452 +RAW_Data: 1771 -2854 3567 -2986 3389 -2992 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1795 -1400 3401 -3002 3393 -1402 1799 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3407 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -7856 8207 -2522 3741 -2764 3573 -2988 3393 -2994 3393 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3006 3399 -2994 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -3002 3401 -2994 3395 -3002 3401 -2998 1805 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 3401 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3601 -4768 1587 -3096 3397 -2990 3395 -2994 3401 -3002 3395 -3006 3397 -2994 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3401 -2998 3399 -2994 3401 -2994 3401 -1402 1795 -3000 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -2994 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -5196 383 -1652 1837 -3050 3375 -2986 3393 -2996 3393 -3002 3401 -2994 3403 -2998 3405 -2994 3395 -3002 3405 -2998 3393 -2996 3401 -3002 3393 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1400 3395 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1398 1795 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -2994 3603 -836 655 -2854 1731 -2816 3487 -2910 3517 -2910 3481 -2856 3443 -3016 3407 -3006 3401 -3006 3399 -2994 3393 -3006 3397 -2996 3393 -3002 3401 -3002 3395 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -3000 3401 -3002 3401 -2994 3407 -1404 1795 -2992 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1799 -1398 3389 -1402 1799 -7638 2209 -2584 3689 -2704 3511 -2934 3347 -3168 3179 -3186 3389 -2994 3395 -2994 3401 -3002 3393 -3004 3393 -3002 +RAW_Data: 3401 -2994 3403 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3407 -3000 3399 -2994 3393 -1402 1795 -2992 1799 -1402 3401 -3002 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -3004 1799 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 3601 -4312 1887 -2802 3527 -2906 3523 -3128 3147 -3164 3181 -3188 3389 -2994 3395 -2994 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1396 1797 -3002 3407 -2996 3395 -2994 3401 -1402 1795 -2998 3395 -3006 1803 -1398 1791 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -7934 8393 -2332 3759 -2774 3583 -2990 3389 -2994 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -2994 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3006 3405 -2998 3397 -1398 1791 -3000 3401 -1402 1799 -2994 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -1400 1801 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -1396 1797 -3002 3603 -4584 1767 -2612 3777 -2734 3539 -2912 3505 -2916 3331 -3156 3173 -3182 3187 -3190 3393 -2996 3401 -3002 3401 -2994 3395 -3002 3401 -2994 3407 -1400 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3401 -1398 1791 -2990 3403 -3002 3401 -3002 3401 -1398 1791 -1398 1801 -3006 1805 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1400 3395 -1400 1801 -1398 1795 -8110 2015 -2632 3843 -2818 3415 -3002 3407 -3004 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2998 3405 -2994 3393 -3004 3393 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1801 -3002 3393 -2994 3401 -3002 3395 -1400 1801 -1402 1799 -2994 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 3397 -1400 1801 -3002 1801 -1396 1793 -1398 3401 -3002 1795 -1398 1801 -1400 3603 -4032 1885 -2658 3649 -2852 3473 -2950 3571 -2718 3513 -2898 3455 -3040 3421 -3010 3401 -3006 3411 -2998 3393 -3002 3395 -3002 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -3008 1799 -1394 1791 -1398 1801 -1400 3401 -3004 3401 -1396 +RAW_Data: 1793 -3002 3407 -2994 3401 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 3401 -1402 1801 -2998 1803 -1398 1791 -1394 3399 -3002 1799 -1402 3401 -8194 21409 -2410 3595 -3000 3401 -3002 3403 -3006 3405 -2994 3395 -3002 3401 -2994 3401 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3407 -1404 1795 -2992 3401 -3002 3395 -3006 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1795 -3000 1799 -1402 1799 -1398 3397 -3002 3403 -3002 3593 -4590 8471 -2416 3813 -2802 3403 -3002 3401 -3002 3401 -3004 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -3002 3401 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -3004 1795 -1398 1799 -1402 1795 -1398 3401 -3004 3401 -1396 1793 -2998 3401 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 +RAW_Data: 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -3006 1803 -1398 1791 -1398 3401 -3004 3393 -1400 1801 -6470 1853 -2954 3275 -3044 3425 -3014 3405 -3004 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2998 3405 -2994 3395 -3006 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3401 -2994 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3000 1803 -1398 1791 -1398 3401 -1402 1799 -3004 1795 -1394 3797 -4442 2001 -2426 3829 -2818 3415 -3014 3401 -3002 3393 -3002 3395 -3002 3401 -3002 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1402 1799 -3006 3399 -2994 3401 -1398 1791 -2998 1801 -1402 1799 -1402 3393 -3002 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -1400 1801 -3002 1801 -1396 1791 -1398 3403 -1400 1801 -3002 3401 -8088 1813 -2902 3441 -3020 3417 -3010 3411 -3004 3399 -3002 3397 -2994 3395 -3002 3393 -3002 3393 -3004 3401 -2998 3405 -2994 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3403 -1400 1795 -2992 3401 -3002 3403 -1400 1795 -2992 1801 -1400 1801 -1400 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -1402 1795 -2992 3601 -4130 1967 -2648 3605 -2874 3687 -2732 3527 -2902 3507 -2930 3349 -3168 3173 -3186 3395 -2998 3397 -2994 3395 -3002 3401 -2994 3401 -3000 3405 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 3401 -1402 1795 -2992 1799 -1402 3401 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -1400 1801 -3002 1801 -1400 1797 -1398 3401 -1400 1801 -1402 1795 -1394 1797 -3402 1061 -2794 2197 -2242 4043 -2320 3991 -2722 3497 -2880 3643 -2830 3415 -3010 3401 -3002 3401 -3002 3403 -3002 3401 -3002 3399 -3006 3393 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3407 -2998 3393 -1396 1793 -3002 3407 -3002 3393 -1398 1795 -3002 1801 -1402 3401 -3002 3393 -3002 1797 -1394 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 +RAW_Data: 1799 -1398 1797 -1400 3401 -1402 1795 -2992 1801 -1400 3401 -3002 1801 -1402 1799 -1398 1791 -1398 3601 -2170 4255 -2912 3537 -2960 3375 -2986 3189 -3192 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3006 3399 -1396 1791 -3000 3401 -3002 3403 -1400 1795 -2992 1801 -1400 3401 -1402 1795 -3000 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -1402 1799 -3002 1797 -1398 3401 -3002 1801 -1396 1793 -1398 3401 -8220 2607 -1888 3817 -2618 3609 -3002 3403 -3002 3401 -3002 3401 -3006 3399 -2994 3401 -2994 3403 -2998 3405 -2994 3393 -3004 3401 -2998 3405 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -2996 1799 -1402 1799 -1398 1797 -1400 3401 -2996 3401 -1400 1801 -2994 3401 -3004 3393 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3401 -1398 1791 -3004 1803 -1402 3393 -3002 1795 -1396 3397 -3002 3401 -4962 8235 -2580 3581 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -2994 3403 -3006 3397 -2998 3405 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3395 -3002 3397 -1404 1797 -2990 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 +RAW_Data: 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 3401 -1398 1791 -3000 1799 -1402 3401 -2998 1805 -1398 3389 -1400 1801 -8008 2567 -1896 3829 -2822 3413 -3014 3407 -3002 3401 -2994 3401 -3002 3395 -3002 3399 -3004 3395 -3002 3399 -3004 3393 -2996 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1797 -2990 3401 -3004 3401 -1400 1801 -2998 3397 -3004 1795 -1394 3397 -3002 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 3403 -1404 1795 -2992 1799 -1402 3401 -2998 3407 -2998 1799 -1394 3791 -4154 1719 -2816 3391 -3170 3285 -3136 3323 -3106 3317 -3134 3147 -3164 3173 -3188 3389 -2994 3395 -3002 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3006 1801 -1394 1791 -1398 1801 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1793 -2998 3401 -3008 3405 -2994 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 1799 -1402 3401 -2994 3403 -2994 3405 -8152 1939 -2656 3635 -2818 3409 -3014 3405 -3002 3403 -2994 3401 -2994 +RAW_Data: 3401 -3004 3393 -3002 3401 -2998 3211 -3198 3393 -2994 3395 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1398 3397 -3002 3401 -1402 1795 -3000 3401 -2994 3403 -1400 1795 -2992 3401 -3006 3407 -1396 1791 -2996 1805 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -2994 1801 -1402 3401 -3002 3393 -1398 1797 -3002 3601 -4348 1837 -2790 3659 -2830 3417 -3016 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3006 3205 -1598 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -3000 1799 -1398 1797 -1400 1801 -1396 3399 -3002 3401 -1398 1795 -3002 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -2998 1805 -1396 1791 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 3399 -1400 1801 -2994 1801 -1400 3401 -3002 3395 -1400 1801 -1398 1791 -8144 1783 -2880 3443 -3018 3419 -3004 3403 -3002 3401 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -2998 3403 -3002 3401 -1400 1797 -2990 3407 -1404 1795 -2992 3401 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -3002 1799 -1398 3397 -1402 1799 -3004 1795 -1394 1797 -1400 4407 -3438 2423 -2508 3661 -2838 3421 -3020 3405 -3002 3401 -3002 3403 -3006 3397 -2994 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3401 -3002 3399 -1400 1791 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3002 3401 -1398 1791 -2998 3403 -1400 1795 -1398 1801 -3002 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -1400 1791 -2992 1801 -1400 3401 -1402 1801 -2994 1799 -1402 3401 -5710 183 -2022 2537 -1884 3815 -2610 3601 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3405 -3006 3393 -2996 3393 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3407 -1400 1791 -2996 3405 -2994 3403 -1396 1791 -3000 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -3008 1803 -1398 3395 -1404 1795 -2990 3403 -3002 3593 -4580 1655 -3024 3321 -2950 3563 -2974 3385 -2992 3397 -2998 3393 -3000 3397 -3002 3393 -2996 3401 -3006 3397 -2994 3403 -3002 3399 -3004 3399 -1400 1789 -1392 1797 -1402 1799 -1402 1799 -1402 1801 -3002 +RAW_Data: 1795 -1394 1791 -1398 1801 -1400 3403 -3002 3401 -1402 1799 -2994 3403 -2994 3401 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1795 -3000 1801 -1400 3401 -1402 1801 -2994 3401 -1400 1797 -7502 3329 -1154 3985 -2602 3601 -3008 3397 -2994 3393 -3002 3407 -2998 3393 -2998 3407 -2994 3401 -2994 3401 -2996 3401 -2994 3401 -2994 3403 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1400 3403 -3002 3401 -1402 1795 -2992 3401 -3002 3401 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 3389 -3004 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -1400 1801 -2994 1801 -1400 4395 -3776 14743 -2408 3811 -2802 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -3002 3401 -2994 3395 -3002 3393 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1398 3397 -3002 3407 -1404 1795 -2992 3407 -2996 3395 -1400 1795 -1398 1801 -3002 1801 -1396 3391 -3002 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 +RAW_Data: 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 3397 -1402 1799 -3002 1797 -1394 3397 -1402 1799 -1402 1799 -2996 3401 -3996 385 -3102 2063 -3036 3275 -3042 3425 -3020 3405 -3006 3411 -2996 3395 -3002 3393 -3002 3395 -3002 3393 -3002 3401 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3407 -1404 1595 -3194 3407 -2994 3393 -1402 1795 -1394 1797 -3002 1799 -1402 3401 -1398 1795 -3004 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 3391 -1400 1801 -1400 1801 -1400 1801 -3002 3603 -1478 645 -1724 2035 -3064 3173 -3182 3187 -3190 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3407 -2998 3393 -3002 3393 -3002 3403 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1402 1795 -1394 3397 -3002 3403 -1400 1795 -3000 3401 -3002 3395 -1400 1801 -1396 1797 -3002 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3407 -1400 1791 -2992 1799 -1402 3401 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -8088 1731 -3044 3437 -3028 3261 -3072 3287 -3118 3295 -3118 3323 -3148 3167 -3180 3185 -3190 3395 -2994 3399 -3004 3393 -2996 3401 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1801 -1396 3397 -2996 3401 -1400 1801 -3006 3403 -2998 3397 -1400 +RAW_Data: 1793 -1394 1795 -3004 3401 -3002 3393 -2994 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1801 -3002 3393 -3002 1801 -1396 1797 -1402 1799 -1402 1795 -1398 3401 -4926 8515 -2468 3839 -2822 3413 -3010 3407 -3004 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3393 -3002 3403 -3002 3401 -2994 3395 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -2994 3403 -3002 3393 -1402 1799 -1398 1795 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 3403 -1396 1791 -3000 3401 -3002 1801 -1400 1797 -1394 1795 -1402 3407 -8138 1617 -2850 3563 -2976 3385 -2990 3395 -2994 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3407 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -3004 1799 -1402 1795 -1398 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -1400 1801 -1398 1791 -2998 3403 -1400 1801 -1400 1801 -2994 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 +RAW_Data: 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -1402 1801 -3002 3397 -3006 1795 -1394 1793 -1398 3401 -3002 3601 -4378 1989 -2806 3493 -2902 3511 -2930 3349 -3166 3183 -3186 3389 -2994 3403 -2994 3407 -3004 3395 -2994 3393 -3002 3401 -3004 3393 -3002 3393 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -3002 3403 -1396 1793 -3002 3405 -2996 3405 -1400 1793 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -2994 1801 -1400 1801 -1402 3393 -1400 1801 -7422 9169 -1802 3795 -2600 3611 -3002 3401 -3006 3405 -2996 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3403 -2994 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 3401 -2996 3401 -1396 1797 -3002 3403 -3002 3393 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 3401 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1795 -3000 3401 -3002 1795 -1394 3399 -3002 1799 -1402 3801 -1286 649 -2066 2205 -3008 3427 -3012 3407 -3006 3393 -2994 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -1402 1795 -2998 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -3002 3401 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 +RAW_Data: 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -3002 3393 -3002 1797 -1394 3397 -3002 3401 -7732 14869 -2460 3833 -2818 3409 -3004 3401 -3002 3401 -3002 3403 -3002 3405 -2998 3393 -3004 3393 -2994 3407 -3008 3199 -1596 1789 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1801 -1398 3397 -3002 3401 -1398 1791 -3000 3401 -2994 3401 -1402 1801 -1400 1801 -1400 1795 -2992 3401 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3407 -1400 1791 -2992 3401 -3004 1799 -1402 3407 -1400 1789 -2988 3403 -3830 2193 -2986 3375 -2944 3501 -3050 3235 -3018 3409 -3002 3403 -3002 3401 -3002 3401 -3002 3395 -3002 3393 -3006 3207 -3194 3399 -3004 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3401 -1398 1795 -3002 3395 -3002 3393 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -3006 1601 -1592 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 3399 -1400 1801 -3002 3401 -2994 +RAW_Data: 1801 -1400 3403 -1400 1795 -1396 1795 -6110 417 -1290 1655 -3016 3535 -2958 3375 -2982 3187 -3194 3399 -2996 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3407 -2996 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1402 1799 -2994 3407 -2998 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 3401 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 3395 -1400 1801 -2994 3401 -3002 3395 -3002 1799 -1398 1791 -1398 3603 -200 965 -3206 1811 -2896 3423 -3010 3405 -3006 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -3002 3401 -2994 3403 -2994 3393 -3002 3407 -2998 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1795 -2990 3403 -3002 3401 -3002 1801 -1396 3391 -7600 1899 -3004 3287 -3106 3315 -3134 3159 -3176 3185 -3190 3391 -2994 3393 -3002 3395 -3002 3401 -2994 3401 -3004 3393 -3002 3407 -2996 3395 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -3002 1795 -1398 1801 -1400 1801 -1402 3401 -2994 3401 -1402 1795 -3000 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 3403 -1404 1795 -2992 3401 -3004 3393 -3002 3401 -2994 3403 -4206 2011 -3272 3159 -3176 3181 -3186 3195 -3190 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3405 -2998 3393 -2994 3403 -2994 3401 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 3403 -1396 1797 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -1404 1597 -3190 3401 -2994 3403 -3002 3393 -1402 1795 -7316 8443 -2572 3577 -2988 3393 -3002 3393 -3004 3393 -3006 3405 -2994 3395 -3002 3393 -3002 3395 -3002 3401 -3002 3393 -3006 3399 -1396 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 3401 -2998 3397 -1398 1791 -3000 3401 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 3401 -1400 1801 -3002 3401 -3004 3393 -1400 1801 -2994 1801 -1400 3603 -4490 1839 -2642 3731 -2746 3573 -2988 3389 -2990 3393 -3000 +RAW_Data: 3405 -2994 3393 -3002 3403 -2994 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3403 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -3000 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -3004 3405 -1396 1793 -2990 1801 -1400 1801 -1402 1799 -1402 1799 -1398 3397 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1400 1801 -3002 3393 -3002 3403 -1396 1793 -2998 3401 -8350 1743 -2940 3389 -3000 3397 -2994 3393 -3004 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3407 -2998 3393 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2998 3407 -1396 1791 -2992 3401 -1402 1801 -3002 1799 -1398 1797 -1400 1801 -1400 1795 -1396 3397 -1400 1801 -3002 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3002 3395 -3002 3407 -1400 1789 -1392 1797 -3006 3605 -4930 1985 -2864 3367 -2982 3185 -3192 3393 -2994 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2998 3207 -3194 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 3407 -2998 3393 -1398 1791 -3000 3401 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 3393 -3002 3407 -1400 1789 -1392 1797 -1402 1799 -8070 9613 -1418 3825 -2614 3609 -3002 3401 -3004 3401 -3002 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -3002 3401 -2994 3403 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1398 1791 -2998 3403 -1400 1801 -3002 1801 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -2996 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -3002 3393 -1402 1795 -3000 1799 -1402 1799 -1398 1791 -1398 3603 -4754 8207 -2562 3569 -2984 3389 -2996 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -2992 3401 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 3401 -1402 1799 -3002 3395 -1400 1801 -2994 1801 -1400 1801 -1398 3389 -7746 8811 -2430 3817 -2810 3411 -3002 3401 -3002 3401 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -3006 3397 -2996 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -2994 3403 -1400 1801 -3002 3401 -1398 1791 -3000 1799 -1402 1801 -1396 1797 -1400 3403 -1396 1791 -1398 1801 -3002 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3004 3393 -1400 1797 -2998 1801 -1400 3403 -3002 3993 -3076 14679 -2816 3619 -2804 3401 -3002 3403 -3002 3393 -3002 3401 -3004 3393 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -2994 3403 -1396 1797 -3002 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3395 -1400 1801 -3002 3393 -1402 1799 -3002 1801 -1398 3389 -1400 1801 -6990 1657 -3042 3285 -3122 3305 -3118 3323 -3152 3167 -3174 3187 -3190 3389 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1801 -1398 3389 -3006 3405 -1398 1791 -3000 3401 -1402 1795 -2990 1801 -1402 1799 -1402 3401 -3002 1795 -1394 3399 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 3401 -1400 1801 -3006 3399 -1396 1797 -3002 3393 -3002 1797 -1394 3597 -4444 1939 -2940 3439 -3026 3413 -3006 3407 -3002 3397 -3006 3393 -2996 3393 -3002 3393 -3006 3207 -3194 3393 -2994 3403 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3393 -1398 1791 -3004 1803 -1402 1795 -1398 3403 -3002 3401 -2998 1799 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1801 -3002 3393 -1402 1799 -3002 3399 -3006 3393 -7892 1731 -2682 3593 -2994 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3399 -3006 3393 -2994 3401 -3006 3399 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1402 1795 -2992 3401 -1402 1799 -3002 1795 -1400 1799 -1402 3401 -2994 3401 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 3401 -1400 1797 -2994 3407 -1400 1795 -3000 3401 -1402 1795 -2992 3601 -4720 1955 -2804 3439 -3022 3413 -3002 3401 -3002 3403 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 1801 -1402 1795 -1394 3397 -1402 +RAW_Data: 1799 -3002 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 3401 -1400 1801 -2994 3403 -1400 1801 -2998 3411 -1400 1789 -1392 1797 -7942 14895 -2618 3611 -2810 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -2998 3407 -2994 3393 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1400 1795 -2992 3401 -1402 1799 -3008 1599 -1594 1795 -1402 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1402 1799 -2994 3403 -1400 1795 -1398 1801 -3002 1795 -1400 1799 -1402 3801 -1294 1075 -840 2397 -2992 3435 -3022 3249 -3164 3313 -2942 3519 -3068 3247 -3026 3413 -3010 3401 -3002 3403 -3002 3393 -3002 3403 -3002 3393 -3002 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -3004 1799 -1398 1791 -1398 1801 -1400 3401 -3004 3401 -1396 1793 -2998 3403 -1400 1801 -3002 1795 -1394 1797 -1400 3403 -1400 1801 -1400 1795 -3000 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 3401 -1402 1801 -2994 3401 -1402 1795 -1398 1799 -3004 1795 -1394 3397 -7240 3691 -1402 3855 -2834 3419 -3018 3413 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3407 -2996 3399 -1404 1597 -1590 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -3002 1801 -1396 1793 -1398 3405 -1404 1797 -1394 1795 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1396 1797 -3002 3401 -1402 1795 -1394 1797 -3002 3407 -3004 3595 -4610 1539 -3058 3381 -2988 3193 -3194 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3407 -3004 3395 -2994 3393 -3002 3401 -2996 3401 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1398 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1801 -2994 1799 -1402 3401 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -3002 3403 -1400 1801 -6986 367 -734 1837 -2952 3531 -2896 3279 -3050 3435 -3018 3401 -3002 3401 -3008 3405 -2998 3205 -3198 3399 -2994 3401 -2998 3399 -2994 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1400 1801 -3002 3395 -1400 1801 -3002 1795 -1394 3397 -3004 1799 -1402 1799 -1398 3395 -3004 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 3399 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 3793 -4322 1655 -3012 3323 -3148 3167 -3186 3189 -3192 3389 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2998 3407 -2994 3401 -2994 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1396 3397 -3004 3401 -1396 1793 -2998 3403 -1400 1801 -3002 1795 -1394 3397 -3002 1801 -1402 3401 -2994 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -1402 1801 -2994 3401 -1398 1795 -1402 1799 -1402 1801 -2994 3401 -3594 835 -1436 377 -1140 1711 -3242 3159 -3176 3181 -3190 3193 -3200 3201 -3202 3197 -3196 3393 -2994 3407 -2996 3395 -2994 3401 -3002 3403 -2994 3401 -2994 3407 -1404 1597 -1592 1791 -1398 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1396 1797 -3002 3403 -1400 1801 -2994 1801 -1400 3401 -3002 1797 -1394 3397 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1402 1799 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3006 3397 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3006 3799 -1526 441 -1060 2475 -2980 3315 -2928 3499 -3076 3249 -3030 3417 -3014 3411 -3006 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -3000 3209 -3202 3197 -1596 1787 -1392 1797 -1400 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1801 -1400 1801 -1396 3399 -3002 3401 -1396 1793 -2998 3403 -1396 1797 -3002 1801 -1400 3401 -3002 3395 -2994 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -7480 27707 -2512 3729 -2756 3573 -2984 3385 -2994 3399 -2990 3393 -3006 3205 -3192 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1400 1797 -2998 3403 -1400 1795 -2992 1801 -1400 3401 -3008 3205 -3194 3393 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -1400 1795 -3000 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 3803 -4446 1433 -3026 3349 -3164 3173 -3188 3189 -3198 3197 -3194 3395 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3401 -2998 3397 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1799 -3004 3393 -1400 1797 -2990 1801 -1400 3403 -3002 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 3393 -7770 21481 -2430 3819 -2810 3401 -3002 3401 -3002 3403 -3002 3401 -3002 3393 -3002 3407 -2998 3393 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -3000 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -3002 3395 -1400 1801 -2994 1801 -1400 3395 -3002 3401 -1402 1795 -1394 1795 -3004 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -1396 1797 -3002 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3795 -3852 2121 -3066 3329 -3156 3167 -3174 3189 -3192 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1801 -3002 3393 -1402 1801 -2994 1799 -1402 3401 -1398 1795 -2996 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1795 -1396 3397 -1400 1801 -7618 +RAW_Data: 15035 -2418 3813 -2810 3403 -3002 3401 -3002 3401 -2996 3401 -3002 3393 -3002 3403 -3002 3393 -2994 3401 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -2996 1805 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -1402 1795 -2992 3401 -1402 1799 -3002 1801 -1402 3393 -1400 1801 -2994 1801 -1400 3403 -2994 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 3401 -3002 1795 -1394 3399 -4894 8261 -2584 3593 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -2996 3405 -3006 3393 -2994 3395 -3002 3401 -2994 3403 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -1400 1801 -2998 1805 -1396 3395 -1404 1795 -2992 3401 -3006 1805 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1396 1797 -1400 1801 -3002 1795 -1394 1797 -1402 3401 -3006 3397 -7586 1729 -3040 3443 -3022 3417 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3000 3405 -2994 3401 -2994 3403 -2998 3205 -3198 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1396 3395 -3006 3393 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 3401 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -1400 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 3401 -1400 1801 -3002 3803 -3690 8945 -2004 3805 -2802 3407 -3006 3401 -3002 3393 -3002 3395 -3002 3401 -2998 3211 -3198 3393 -2994 3395 -3002 3401 -3002 3393 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3008 1803 -1398 1791 -1394 1797 -1400 3401 -3004 3401 -1400 1801 -3002 3393 -1402 1801 -2994 1799 -1402 3401 -1398 1797 -1396 1797 -3002 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1396 3399 -1400 1801 -1400 1801 -8146 15111 -2438 3821 -2814 3409 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3407 -1404 1597 -1592 1793 -1398 1799 -1402 1799 -1402 1801 -2994 1801 -1400 1801 -1396 1797 -1400 3407 -3006 3393 -1398 1791 -3000 3401 -1400 1801 -2994 1801 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -3004 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1400 1795 -1394 1797 -3002 1801 -1400 3407 -3006 1595 -1592 1789 -1398 3603 -4270 1767 -2816 3481 -2910 3319 -3118 3293 -3244 3229 -3014 3409 -3006 3407 -3002 3407 -3004 3395 -2994 3401 -2994 3401 -2996 3401 -3002 3399 -1404 +RAW_Data: 1595 -1594 1797 -1400 1801 -1400 1797 -1398 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3006 3207 -1596 1791 -2992 3401 -1402 1801 -3002 1795 -1398 3401 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3401 -1402 1795 -1394 1797 -3002 1801 -1400 3407 -3004 1597 -1594 3393 -8044 8285 -2600 3601 -3004 3401 -2994 3401 -2994 3403 -3002 3407 -2996 3393 -3004 3393 -3002 3399 -3004 3395 -2994 3401 -3002 3399 -1404 1597 -1592 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1799 -1398 1797 -1396 1797 -1400 3407 -3006 3393 -1398 1791 -3000 3405 -1404 1597 -3190 1801 -1400 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1396 1791 -1400 1799 -3002 1801 -1400 3403 -3002 3393 -3002 4003 -3520 1767 -3000 3331 -3084 3243 -3026 3409 -3002 3407 -3010 3405 -2998 3397 -2996 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3403 -2994 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -2994 3407 -1404 1793 -2988 3403 -3002 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 +RAW_Data: 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -3004 1799 -1402 3393 -3002 3393 -1402 1801 -4796 787 -2262 1869 -2942 3419 -3010 3405 -3006 3393 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -3002 3399 -3010 3193 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -3000 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1400 1797 -1394 3397 -3002 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 3401 -1398 1791 -1398 1801 -3002 1799 -1402 3393 -1402 1799 -3004 1799 -1398 4199 -2960 2227 -2872 3355 -3174 3183 -3186 3389 -2994 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3395 -3002 1795 -1398 1801 -1400 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -3004 1799 -1398 3389 -1402 1799 -3002 3403 -7954 27839 -2554 3573 -2988 3389 -2994 3393 -3002 3395 -3002 3399 -3004 3395 -2994 3393 -3002 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1400 1801 -3002 3395 -1400 +RAW_Data: 1801 -2994 3401 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -3002 1801 -1400 3401 -1398 1797 -1400 1801 -3002 3393 -4822 15921 -1406 3809 -2610 3601 -3002 3403 -3002 3401 -3002 3401 -3004 3393 -2994 3407 -3004 3395 -2994 3407 -2996 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3399 -1404 1595 -3190 3403 -1400 1801 -3002 3393 -3006 1601 -1594 3389 -3002 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -1396 1791 -1398 1801 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -1402 1799 -6668 1963 -2566 3673 -2838 3421 -3014 3411 -3006 3393 -3002 3393 -3004 3401 -2994 3407 -2996 3395 -3002 3393 -3002 3395 -3002 3405 -2998 3399 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -3002 3393 -3002 1797 -1394 3397 -3002 3401 -3002 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3407 -1400 1791 -1394 1797 -3002 3401 -3002 1797 -1394 1795 -1402 1801 -1400 3803 -4242 1899 -2672 3763 -2746 3509 -2864 3439 -3026 3417 -3002 3401 -3008 3405 -3002 3393 -2994 3403 -3002 3405 -2998 3393 -2996 3401 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -1398 1797 -3002 3401 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 3401 -1398 1791 -1398 1801 -3002 3401 -3002 1795 -1394 1797 -1402 3401 -7512 21165 -2482 3841 -2832 3413 -3010 3401 -3002 3403 -3002 3401 -3002 3407 -2998 3393 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -3002 3395 -1400 1797 -2990 3401 -3004 1799 -1402 3401 -1398 1791 -1398 1799 -3004 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1795 -1398 3403 -1400 1801 -1400 1797 -2998 3395 -3002 1799 -1402 3401 -3002 3399 -3900 661 -442 1987 -2846 3333 -3156 3167 -3182 3185 -3192 3393 -3002 3393 -3004 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3401 -2996 3393 -3002 3401 -1402 1795 -1398 1795 -1398 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -2994 3401 -1402 1801 -2994 3407 -3004 3193 -3196 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 3407 -1400 1789 -1392 1797 -3002 3401 -3002 1801 -1402 3393 -1400 1801 -5854 1449 -3028 3481 -2910 3519 -2928 3493 -2856 3433 -3014 3415 -3010 3405 -2998 3397 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -1404 1595 -1588 1795 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1402 1799 -2994 3403 -1400 1801 -2994 3393 -3002 3403 -3002 1795 -1394 3397 -3002 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -1400 1801 -2994 3401 -2996 3401 -3002 1801 -1396 4197 -2116 2985 -3040 3495 -2924 3333 -3160 3173 -3184 3189 -3198 3189 -3196 3393 -3002 3393 -3002 3407 -2998 3393 -3002 3395 -3002 3401 -2994 3393 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1400 1801 -3002 3395 -1400 1801 -2994 3407 -2996 3395 -3002 3401 -2994 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3395 -1400 1801 -1400 1795 -2992 3401 -3006 +RAW_Data: 3207 -3194 3407 -3798 1471 -2756 1731 -2764 3451 -3026 3423 -3014 3401 -3002 3393 -3008 3401 -2998 3393 -3002 3403 -2994 3401 -2994 3395 -3002 3401 -3002 3393 -3002 3395 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3405 -2998 3393 -1398 1797 -3002 3393 -1400 1801 -3002 3395 -3002 3401 -2998 3209 -1602 1591 -3184 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 3401 -1400 1801 -1398 1791 -3000 3401 -3002 3401 -1398 1795 -3002 3603 -3528 2573 -2892 3567 -2982 3387 -2990 3393 -2994 3407 -3006 3393 -2994 3401 -2996 3393 -3002 3401 -3002 3403 -2994 3401 -2994 3399 -3006 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1795 -1394 3403 -3004 3401 -1398 1791 -3000 3401 -1402 1795 -3000 3401 -3006 3403 -1400 1791 -2992 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 3401 -1398 1795 -1402 1801 -3006 3197 -3194 3393 -1402 1801 -1400 1801 -7732 2037 -2694 3601 -2994 3401 -2994 3403 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -3002 3399 -3004 3399 -2998 3399 -2996 3395 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1396 3399 -3002 3393 -1402 1799 -3002 3395 -1396 1797 -3006 3397 -3002 3395 -1400 1801 -3002 3393 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -1396 1797 -1400 1801 -3002 3393 -1402 1795 -3004 1803 -1398 1791 -1398 3403 -4578 1767 -2596 3785 -2526 3699 -2874 3441 -3022 3419 -3010 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3407 -1404 1595 -3192 3401 -1396 1793 -2998 3403 -3002 3393 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1396 1797 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1398 3401 -7634 8423 -2600 3601 -3002 3403 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1396 1793 -2998 3403 -1400 1801 -3002 3407 -2996 3395 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 3401 -1402 1795 -1398 1801 -3002 3401 -1398 1795 -3004 3401 -2994 3601 -5084 8583 -2458 3835 -2822 3413 -3010 3403 -3002 3401 -3002 3395 -3002 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -3002 +RAW_Data: 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3407 -1404 1595 -1594 1791 -3000 3405 -1404 1597 -3190 3407 -1404 1597 -7988 1673 -3042 3323 -3148 3363 -2976 3185 -3194 3399 -2998 3393 -3002 3401 -2994 3395 -3006 3397 -2994 3403 -2994 3407 -3004 3393 -2996 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 3403 -3002 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1398 1791 -3000 3401 -1400 1801 -1400 1801 -3002 1795 -1396 3597 -4754 1619 -2850 3567 -2982 3389 -2990 3395 -3002 3393 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3407 -2996 3395 -2998 3397 -2994 3403 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -1400 1801 -2994 3401 -1402 1795 -2992 1799 -1402 3401 -3002 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 +RAW_Data: 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -1402 1795 -1398 1801 -3002 3401 -7874 2005 -2696 3647 -2838 3423 -3002 3401 -3002 3401 -3004 3401 -3006 3397 -3002 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3407 -2998 3397 -1400 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1398 3401 -3006 3393 -1398 1791 -3000 3401 -1402 1799 -2994 3403 -1400 1795 -2992 1801 -1400 3401 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 3401 -1396 1797 -1402 1799 -2994 3403 -1400 1795 -1396 1795 -1402 1799 -3004 3801 -4428 14941 -2434 3819 -2810 3415 -3004 3403 -3002 3405 -2998 3393 -3000 3397 -3002 3393 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -3004 1803 -1398 1791 -1398 1801 -1400 3407 -2998 3393 -1402 1799 -3002 3395 -1400 1801 -2994 3401 -1398 1797 -3002 3401 -2998 1805 -1396 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 3395 -1400 1801 -1400 1801 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -7354 2019 -2886 3413 -3002 3401 -3002 3403 -3002 3401 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3407 -2996 3395 -2994 3393 -3002 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -3004 +RAW_Data: 3205 -1600 1591 -3188 3403 -1400 1801 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -2994 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3401 -1402 1801 -1396 1791 -1398 1801 -3006 1805 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -1476 3223 -2870 3519 -2934 3559 -2974 3187 -3190 3389 -2994 3395 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3407 -3004 3395 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1396 1797 -1400 3403 -3002 3401 -1398 1791 -3000 3401 -1400 1801 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -7212 2943 -1710 3963 -2582 3587 -2990 3389 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3401 -3002 3399 -3006 3393 -2994 3401 -2996 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1396 1797 -1402 3401 -3002 3393 -1402 1801 -2994 3401 -1402 1795 -2990 3403 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1400 1801 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 3401 -3002 3795 -412 421 -3620 8315 -2598 3797 -2810 3401 -3002 3403 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3002 3395 -3002 3407 -2996 3395 -3002 3393 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1799 -1402 1801 -1400 3395 -1400 1801 -7154 1989 -2800 3335 -3110 3489 -3052 3239 -3024 3411 -3006 3393 -3002 3401 -3002 3395 -3006 3397 -2994 3407 -3002 3197 -3194 3395 -3002 3393 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1396 1797 -3006 1605 -1596 3391 -3002 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 3401 -1402 1795 -1394 1795 -1402 1801 -3002 1799 -1402 3401 -2994 1801 -1402 3393 -4578 1987 -2610 3585 -2746 3685 -2908 3493 -2922 3337 -3160 3375 -2982 3389 -2990 3395 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -3002 3395 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3004 3405 -1396 1793 -2998 3403 -1400 1801 -1396 +RAW_Data: 1793 -2998 3401 -3004 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 3401 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 3401 -2994 3401 -7890 1857 -2976 3401 -3002 3401 -3002 3403 -2994 3401 -3006 3397 -2996 3405 -3002 3197 -3194 3395 -3002 3205 -3198 3395 -2994 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1795 -1394 3403 -3006 3393 -1402 1799 -2998 3207 -1596 1791 -2996 3405 -1402 1795 -1398 1801 -3006 3397 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 3401 -1400 1801 -3006 3397 -4660 9619 -1422 3829 -2818 3415 -3014 3405 -3002 3401 -3002 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -3002 3393 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -3002 3403 -1396 1793 -2998 3401 -1402 1801 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -3006 1803 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -4378 191 -1688 551 -1116 1659 -3106 3309 -3094 3259 -3038 3421 -3012 3405 -3006 3401 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1795 -1394 1795 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1801 -3002 3401 -2994 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -3002 3393 -3002 1797 -1394 1795 -1402 3401 -4704 2681 -1944 3813 -2610 3603 -3002 3401 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -2998 3399 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -3000 1801 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -1402 1799 -2996 3401 -1400 1797 -2990 3407 -1404 1801 -1396 1793 -1398 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 3397 -1400 1801 -1402 1795 -1394 1797 -3002 3401 -3002 1801 -1400 3395 -8456 8271 -2610 3601 -3002 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3006 3399 -2994 3401 -2994 3403 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -3002 1805 -1396 1797 -1402 1799 -1402 3401 -2998 3205 -1598 1791 -2992 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 +RAW_Data: 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -3002 3393 -3002 3395 -3002 3601 -3880 2553 -2922 3559 -2976 3381 -2988 3389 -2994 3401 -3004 3393 -2994 3401 -3002 3203 -3198 3397 -2994 3403 -2994 3401 -3002 3407 -2996 3395 -1396 1797 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -1402 1799 -3006 1601 -1594 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 3389 -1400 1801 -1402 1799 -1402 1799 -3004 3393 -3002 3393 -1402 1801 -4436 421 -3014 1755 -3064 3393 -2996 3401 -2998 3205 -3194 3403 -2998 3397 -2994 3403 -3002 3393 -2994 3401 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -1402 1799 -1402 1799 -1402 1801 -2994 3401 -1398 1795 -3006 1605 -1598 4387 -1926 735 -564 1715 -3138 3393 -2994 3403 -3006 3197 -3194 3393 -3004 3401 -3002 3393 -3002 3395 -2994 3401 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1402 3393 -3006 3205 -1598 1791 -2992 3401 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 3401 -1400 1801 -1398 1791 -1398 1799 -3004 3401 -1400 1797 -2998 3401 -7510 1767 -2584 3763 -2696 3659 -2834 3419 -3010 3409 -3002 3401 -3002 3403 -3006 3397 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3407 -3004 3195 -1596 1791 -2992 3401 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1795 -1398 3403 -1400 1801 -2994 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 3401 -1402 1799 -1402 1795 -1394 1797 -3002 3401 -1402 1799 -1402 1795 -2992 3601 -4408 9397 -1404 3805 -2602 3601 -3002 3401 -3006 3407 -2998 3405 -2994 3395 -3006 3397 -2994 3395 -3002 3401 -2994 3401 -2994 3403 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1402 3393 -3006 3397 -1402 1795 -2992 3401 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 3401 -1396 1797 -1402 1799 -1402 1799 -2996 3401 -1400 1797 -1394 1795 -1402 1799 -7782 2983 -1722 3737 -2764 3575 -2986 3389 -2994 3395 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3401 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1795 -1394 3397 -3002 3403 -1400 1795 -3004 3205 -1598 1791 -1394 1797 -3002 1801 -1400 1801 -1400 3401 -3004 3393 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -3002 1801 -1400 1801 -1402 1799 -1398 3789 -3630 9707 -1196 3995 -2602 3601 -3004 3401 -3002 3393 -3002 3403 -2994 3407 -3004 3193 -3200 3397 -2994 3401 -2996 3401 -3002 3393 -1402 1799 -1402 1795 -1398 1795 -1394 1797 -1402 1799 -3002 1801 -1402 1799 -1402 1799 -1398 3397 -3002 3407 -1400 1791 -2992 3401 -1402 1801 -1400 1795 -3000 1801 -1400 1795 -1398 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 3401 -7046 2191 -2780 3479 -2930 3487 -3108 3315 -2934 3349 -3166 3383 -2990 3389 -2994 3395 -3002 3401 -2998 3399 -2994 3401 -3002 3393 -3004 3393 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2996 3409 -1400 1789 -1394 1795 -3004 1799 -1402 1799 -1402 3407 -1404 1595 -1594 1791 -3000 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 3401 -2996 3401 -5646 14913 -2416 3815 -2810 3401 -3002 3401 -3006 3407 -2994 3401 -2998 3205 -3196 3201 -3194 3393 -3008 3205 -3194 3393 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -3004 1799 -1402 1799 -1402 1795 -1398 3401 -3002 3395 -1400 1797 -2998 3401 -1402 1801 -1396 1791 -3000 1801 -1400 3401 -3004 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 3395 -1400 1801 -6192 2021 -2460 3747 -2764 3583 -2986 3389 -2994 3395 -3002 3393 -3002 3403 -2994 3405 -3002 3197 -3196 3393 -3002 3393 -3002 3403 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1795 -1398 1801 -3002 1795 -1398 3401 -3002 1797 -1398 3401 -3002 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 +RAW_Data: 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3401 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -3002 3401 -3002 1797 -1398 3601 -4152 2483 -2464 3741 -2764 3575 -2986 3385 -2994 3395 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -3002 3393 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3006 1601 -1592 1793 -1394 1795 -1402 3401 -3002 3403 -1400 1795 -3000 3401 -1398 1795 -1402 1801 -3006 1599 -1594 3389 -3002 3403 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1402 1799 -1402 3393 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -3002 3393 -3002 3403 -5836 199 -800 2239 -2970 3487 -2904 3313 -3324 3147 -3168 3173 -3188 3389 -2998 3397 -2996 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -3000 3205 -1596 1793 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -1398 1795 -3004 1799 -1402 3393 -3006 3205 -1598 1791 -2992 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 3401 -1402 1799 -1402 1799 -1402 1795 -1394 +RAW_Data: 1797 -3002 3401 -1402 1799 -3004 4001 -1452 1311 -1732 1679 -2834 3579 -2986 3395 -2998 3189 -3198 3199 -3194 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3401 -2994 3403 -2994 3401 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1402 1799 -1402 1799 -3000 1799 -1394 3397 -1402 1799 -3002 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 3389 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -7878 14949 -2414 3809 -2802 3401 -3004 3401 -3002 3401 -3002 3395 -2994 3401 -3002 3393 -3004 3401 -2998 3205 -3194 3395 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3403 -1404 1595 -3188 3401 -1400 1801 -1398 1791 -2998 1801 -1402 3401 -1400 1801 -2994 3403 -2994 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 3401 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1797 -1400 3603 -4378 1987 -2808 3349 -2882 3487 -3116 3325 -2936 3561 -2974 3385 -2992 3389 -2994 3393 -3004 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1400 1797 -2990 3403 -1400 1801 -1400 1801 -3006 1601 -1592 3391 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 +RAW_Data: 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 3399 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -3004 1803 -1398 3389 -7766 3599 -1180 3997 -2602 3601 -3002 3401 -3002 3403 -2994 3401 -3002 3401 -2996 3393 -3006 3397 -2994 3403 -3002 3401 -2994 3403 -2994 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3403 -1400 1801 -2994 3401 -1398 1791 -1398 1801 -3002 1799 -1402 3401 -1402 1799 -1398 1791 -1398 1801 -3002 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2990 3401 -3004 3801 -4548 2039 -2540 3875 -2646 3423 -3026 3413 -3006 3405 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -2998 3393 -2994 3401 -3000 3205 -1596 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1400 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -1402 1799 -2996 3401 -2994 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -3002 3407 -1404 +RAW_Data: 1595 -7378 2189 -2976 3371 -2942 3489 -2908 3291 -3120 3341 -3164 3173 -3188 3189 -3190 3393 -3006 3399 -2998 3205 -3194 3395 -2994 3401 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2994 3407 -1396 1791 -1398 1801 -3002 3403 -2994 1799 -1402 1801 -1400 3401 -2994 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -2994 1801 -1400 3401 -4138 9989 -410 4445 -2630 3617 -2814 3415 -3006 3401 -3002 3401 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -3006 3197 -1598 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -3006 1605 -1596 1793 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -1402 1801 -3002 3393 -3002 1795 -1394 3399 -3002 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -3002 3395 -7518 2025 -2782 3651 -2826 3417 -3010 3407 -3010 3409 -2998 3389 -2996 3397 -3010 3397 -2994 3203 -3194 3393 -3002 3395 -3002 3401 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -3006 3397 -1398 1791 -1398 1801 -3002 3401 -2994 1801 -1402 3401 -1400 1597 -3190 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 +RAW_Data: 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -2994 4001 -3412 1945 -2826 3517 -2924 3347 -3164 3381 -2988 3389 -2994 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -3002 3405 -1398 1797 -1400 1801 -3002 3393 -3006 3399 -2994 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 3397 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 3601 -1280 5101 -3078 3271 -3238 3223 -3014 3409 -3006 3405 -2996 3401 -3002 3401 -2994 3395 -3002 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3395 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -3002 3395 -1400 1797 -1394 1795 -3002 3403 -3002 3393 -3006 3205 -3196 1595 -1594 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -7728 1751 -3100 3263 -3030 3413 -3014 3415 -3006 3209 -3202 +RAW_Data: 3193 -3200 3197 -3194 3393 -2998 3407 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1400 1801 -3002 3403 -1396 1791 -1398 1801 -3002 3401 -2996 3401 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 3401 -3004 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1797 -1398 3401 -3002 4405 -2758 2807 -2942 3325 -2938 3367 -3180 3379 -2990 3189 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2998 3205 -3196 3393 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -3004 1799 -1402 1799 -1402 1801 -1396 3397 -3004 3393 -1400 1801 -3002 3395 -1400 1801 -1400 1795 -2996 3405 -2996 3401 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 3401 -1400 1801 -5956 861 -1316 1441 -3046 3355 -3176 3381 -2988 3389 -2994 3201 -3196 3393 -3002 3401 -2994 3403 -2994 3401 -3006 3397 -2996 3401 -2994 3401 -2994 3403 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -3002 1801 -1400 1801 -1400 1801 -1398 3389 -3002 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 3403 -3002 1801 -1400 1801 -1400 1795 -1396 1795 -1402 3401 -3002 1801 -1396 3991 -4480 15153 -2008 3809 -2602 3603 -3002 3401 -3002 3407 -3006 3393 -2994 3393 -3004 3405 -2998 3393 -3002 3395 -3002 3393 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -2994 1805 -1396 1797 -1402 1795 -1398 3401 -3002 3395 -1400 1801 -3006 3397 -1398 1791 -1398 1801 -3002 3401 -1398 1795 -3002 1801 -1402 3401 -3002 1597 -1592 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1597 -1592 1797 -1402 1799 -1402 3401 -3002 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3002 3395 -7734 2455 -2034 3851 -2834 3419 -3014 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -2996 3401 -3002 3407 -2996 3399 -1404 1795 -1392 1795 -1402 1799 -1398 1797 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1398 1791 -3000 3401 -1400 1801 -1398 1791 -2998 3403 -1400 1801 -3002 3401 -2994 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -1398 1791 -3002 3607 -3594 2481 -2584 3775 -2732 3541 -2914 3491 -2916 3333 -3160 3375 -2982 3389 -2992 3393 -2994 3401 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1396 1797 -1400 1801 -3002 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3395 -1400 1801 -2994 3401 -1402 1799 -1398 1791 -3000 3401 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -1402 1801 -1400 1801 -7478 1559 -3038 3563 -2982 3391 -2992 3395 -3002 3399 -2996 3391 -2994 3393 -2994 3403 -3002 3407 -3004 3393 -2996 3393 -3002 3401 -2994 3403 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1396 1797 -1402 3401 -2994 3401 -1402 1799 -2996 3401 -1400 1801 -1402 1795 -2990 3407 -1404 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -2998 1603 -1598 1791 -1394 1797 -1402 3401 -3006 1605 -1596 1791 -1394 3399 -4468 1663 -3040 3363 -2940 3339 -3112 3293 -3122 3329 -3156 3367 -2982 3387 -2990 3393 -2994 3403 -3002 3401 -2994 3401 -3002 3395 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3006 3207 -1594 1789 -2992 3401 -1402 1801 -1400 1801 -2994 3401 -1402 1799 -1398 1797 -3002 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 3401 -3002 1801 -1398 1795 -1402 1799 -1398 3403 -3004 1597 -1594 3389 -3402 1499 -440 419 -600 599 -766 1693 -3100 3401 -3002 3395 -2994 3401 -3002 3395 -3002 3393 -3006 3205 -3200 3197 -3194 3201 -3194 3403 -2994 3401 -3002 3395 -3006 3397 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3393 -3002 3401 -1398 1797 -3002 3401 -1398 1795 -1398 1797 -3002 3401 -1400 1797 -1398 1799 -1402 1801 -2994 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -2996 4203 -806 201 -2874 1835 -2990 3405 -2996 3393 -3002 3393 -3004 3405 -2998 3393 -2994 3403 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -3002 1801 -1398 1795 -1402 1799 -1398 3397 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -3006 1605 -1596 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3395 -1400 1801 -6000 21381 -2268 3645 -2836 3417 -3010 3407 -3008 3403 -2998 3399 -2996 3395 -2994 3401 -2994 3401 -2996 3401 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1394 1797 -3002 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 3397 -3002 3401 -1398 1791 -3000 3405 -1406 1795 -1394 1795 -1402 1801 -3006 1599 -1594 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 3401 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -1398 1795 -3002 1801 -1402 3601 -4730 8239 -2608 3611 -3002 3401 -3002 3401 -3002 3395 -3002 3401 -2998 3399 -3002 3393 -3002 3401 -2996 3401 -2994 3407 -2996 3399 -1404 1795 -1396 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1398 1791 -3000 3401 -1400 1801 -1398 1795 -1402 1801 -3002 1795 -1398 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -2994 1801 -1400 1801 -1396 1793 -1398 3401 -1400 1801 -3006 3397 -7406 3789 -200 4429 -2816 3413 -3014 3407 -3002 3407 -3004 3393 -3000 3397 -2994 3401 -2996 3401 -3002 3399 -3004 3395 -2994 3401 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1396 1793 -1398 1799 -3006 1805 -1398 1791 -1398 3401 -3002 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 +RAW_Data: 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -4904 1617 -2646 3741 -2764 3575 -2986 3385 -2994 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -2994 3401 -1402 1801 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -3002 1801 -1400 1801 -1396 3399 -1400 1801 -3002 1595 -1594 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 1801 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -1402 1795 -6010 3547 -1150 3953 -2576 3585 -2990 3395 -2994 3393 -3002 3403 -3002 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1400 1797 -2998 3407 -1404 1597 -1592 1793 -1398 1799 -3002 1801 -1402 3401 -2998 1605 -1596 1791 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -2994 1799 -1402 1801 -1400 3401 -2998 1805 -1398 1791 -1398 1801 -1400 3601 -4812 1887 -2884 3391 -2994 3393 -2994 3395 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -3002 3395 -3002 3201 -3198 3205 -3196 3393 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1400 3393 -3004 +RAW_Data: 3393 -1400 1801 -3002 3395 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 3403 -2994 3401 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3002 1797 -1394 1795 -1402 3401 -3002 1801 -1398 1791 -1398 3401 -4786 197 -1694 1655 -3046 3275 -3160 3351 -3136 3317 -2902 3311 -3324 3147 -3168 3181 -3186 3191 -3194 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -3002 3401 -1398 1791 -3004 3405 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1398 3397 -1402 1799 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 3401 -2998 1805 -1396 1791 -1394 3399 -3002 1801 -1400 3401 -3002 4219 -3452 1989 -2654 3675 -2776 3467 -2882 3487 -3110 3319 -2932 3561 -2974 3381 -2992 3393 -2998 3393 -2996 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1402 3401 -2994 3403 -1400 1795 -2992 3401 -1402 1799 -1402 1801 -1400 1795 -2992 1801 -1396 3397 -1402 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -2998 1805 -1400 1797 -1394 3397 -3002 1801 -1396 3399 -1400 1801 -7622 2021 -2904 3499 -2872 3443 -3018 3413 -3006 3411 -3006 3397 -3006 3393 -2994 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2994 3403 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -2998 1605 -1596 1791 -1394 1797 -1402 3401 -3002 3401 -1402 1799 -3004 3393 -1396 1797 -1402 1799 -1402 1801 -2994 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 3397 -3002 1801 -1396 1797 -1400 3403 -3002 3393 -3002 1801 -1396 4405 -3580 2203 -2680 3433 -3018 3415 -3006 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3203 -3194 3399 -3004 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -2998 3205 -1598 1791 -2998 3403 -1400 1801 -1396 1793 -1398 1799 -3002 3403 -3006 1599 -1592 3401 -3004 1597 -1594 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 3401 -2996 3401 -2994 3401 -7234 3089 -2524 3707 -2718 3733 -2896 3467 -2840 3421 -3020 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3407 -2998 3393 -3002 3399 -2996 3399 -1404 1795 -1394 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1400 1795 -1394 1793 -1398 3401 -3002 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -3004 3401 -2994 3401 -2998 1805 -1398 +RAW_Data: 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1595 -1594 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 3401 -3006 1599 -1594 1791 -1398 3403 -3002 3401 -1398 1791 -2998 4003 -4646 1945 -2714 3615 -2814 3409 -3002 3407 -3004 3395 -3006 3397 -2994 3395 -3002 3401 -3002 3393 -3004 3401 -2994 3393 -3002 3403 -2994 3401 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1402 3401 -2994 3395 -1400 1801 -3002 3393 -1402 1795 -1398 1801 -1400 1801 -2990 3401 -3002 3403 -1400 1801 -3002 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 1801 -1396 1797 -1402 3401 -3002 3393 -1398 1797 -1400 1801 -7714 15297 -1990 4001 -2594 3603 -2994 3401 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -2994 3407 -3006 3393 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2998 1801 -1396 1797 -1402 1799 -1402 3401 -2994 3401 -1402 1795 -3000 3401 -1402 1795 -1398 1799 -1398 1797 -3002 3401 -1402 1799 -2994 1801 -1402 1799 -1402 1595 -1594 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 3401 -3002 1801 -1396 1797 -1402 3401 -1396 1797 -3002 1801 -1400 1801 -1398 3389 -4226 1665 -3024 3337 -3164 3373 -2986 3387 -2994 3393 -3002 3395 -3002 3407 -2996 3395 -2994 3401 -3002 3393 -2996 3401 -3002 3401 -2998 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1398 1797 -1400 1801 -1400 3407 -3006 3193 -1598 1791 -3000 3401 -1396 1797 -1402 1799 -1402 1799 -3002 3395 -1396 1797 -3002 3401 -2996 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1597 -1592 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 3403 -2998 1603 -1598 1791 -1394 3399 -1400 1801 -3002 1801 -1400 3401 -7946 8393 -2610 3601 -3002 3403 -3002 3401 -3002 3393 -3004 3401 -3002 3393 -2994 3407 -3006 3393 -2994 3401 -2996 3401 -2994 3401 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3002 1801 -1398 1791 -1394 1797 -1400 3407 -3006 3401 -1396 1793 -2998 3407 -1404 1795 -1394 1797 -1400 1801 -3002 3403 -1396 1791 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1595 -1594 1795 -1402 3401 -1402 1799 -3004 3393 -3002 4001 -3612 15189 -2202 3801 -2602 3601 -3004 3401 -3002 3401 -2994 3403 -2994 3407 -2996 3399 -3010 3397 -2990 3393 -3004 3393 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -1402 1799 -1402 1795 -2992 3401 -1402 1799 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 +RAW_Data: 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3401 -3006 1601 -1596 1791 -1398 3403 -1400 1801 -3002 3393 -1402 1801 -4208 1657 -1960 1633 -3076 3349 -3166 3183 -3186 3189 -3194 3395 -3002 3401 -2994 3403 -2994 3401 -3002 3399 -3004 3395 -3002 3393 -2994 3403 -3002 3201 -1598 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3401 -2994 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 3395 -3004 1601 -1596 1793 -1398 3401 -1400 1801 -1402 1799 -3002 1797 -1394 3797 -4466 8859 -2000 3797 -2810 3401 -3002 3401 -3002 3403 -3002 3393 -2994 3403 -3002 3401 -3002 3393 -3002 3407 -2998 3399 -2996 3395 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -2998 3205 -1598 1791 -3000 3401 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 3401 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 3403 -2994 1799 -1402 1795 -1394 +RAW_Data: 3399 -1400 1801 -1400 1801 -3002 3205 -4194 389 -3580 1837 -3274 3173 -3082 3345 -3118 3293 -3118 3317 -3142 3163 -3174 3183 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3205 -3198 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 3403 -2994 3401 -1400 1801 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1595 -1594 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3401 -3004 1799 -1402 1795 -1398 3401 -1398 1797 -1400 1801 -1400 1801 -2994 4407 -3396 2177 -2946 3427 -3018 3407 -3004 3407 -3006 3389 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3395 -3002 3393 -3002 3393 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -2996 1799 -1402 1795 -1398 1801 -1400 3407 -3004 3395 -1396 1793 -2998 3407 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -3002 1801 -1402 3401 -1396 1793 -2998 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -7678 1989 -2426 3963 -2528 3683 -2904 3519 -2930 3347 -3168 3373 -2988 3389 -2994 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3393 -1400 1801 -2994 3403 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1599 -1598 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1601 -1598 1791 -1398 1799 -1402 1801 -1396 3397 -3004 1799 -1402 3401 -2994 1801 -1400 1601 -1598 1791 -1398 1801 -1400 3401 -4800 1839 -2646 3737 -2764 3573 -2988 3385 -2994 3395 -3002 3393 -3002 3401 -2994 3403 -3002 3393 -2994 3407 -3006 3393 -2994 3401 -3004 3393 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3405 -3006 3389 -1398 1797 -3002 3405 -1402 1789 -1392 1797 -1400 1801 -1400 1801 -3002 3401 -3002 3395 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 3401 -2994 1801 -1400 3403 -3002 1595 -1594 1797 -1400 1801 -1400 3403 -7764 1463 -3208 3319 -2936 3563 -2976 3385 -2986 3391 -2994 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1797 -1402 1799 -1402 3405 -2998 3399 -1404 1597 -3190 3401 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -3002 3401 -1402 1801 -2994 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -3002 1801 -1396 3391 -3002 1799 -1402 1801 -1400 3401 -2996 3401 -1706 +RAW_Data: 219 -2872 1657 -2822 3519 -2942 3559 -2974 3385 -2992 3389 -3002 3401 -2994 3403 -2994 3407 -2996 3395 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3000 1803 -1398 1791 -1398 1801 -1400 3401 -3004 3393 -1400 1801 -2994 3401 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -3002 3403 -1400 1597 -1592 1797 -3002 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 3401 -2994 1801 -1402 3401 -2994 1801 -1400 1795 -1396 3397 -1400 1801 -7342 2387 -2176 3817 -2614 3601 -3002 3403 -3002 3401 -3002 3401 -2996 3405 -2998 3393 -3002 3395 -3002 3393 -3002 3407 -2998 3401 -2994 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1400 1597 -3190 3403 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1799 -1398 3389 -3002 1801 -1400 3395 -3002 1799 -1402 3593 -4558 1793 -2438 3923 -2740 3563 -2976 3385 -2990 3395 -2994 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -2994 3407 -2998 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -2994 1801 -1402 1799 -1402 1799 -1398 3389 -3002 3403 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -1396 1797 -1400 1801 -1402 1799 -3002 1801 -1398 3397 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1597 -1592 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3401 -2994 1801 -1400 3403 -3002 1595 -1594 3397 -3002 3395 -7924 1529 -3076 3409 -3002 3403 -3002 3401 -3002 3401 -2996 3393 -3002 3401 -2994 3407 -2998 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3399 -1404 1795 -1394 1793 -1398 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1801 -1400 1801 -1402 3397 -3010 3197 -1598 1791 -2992 3401 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 3401 -3000 1603 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -3002 1801 -1400 3401 -2996 1799 -1402 3407 -1400 1591 -3192 3601 -4992 1735 -2952 3401 -3006 3401 -3002 3403 -3002 3401 -2998 3397 -2996 3405 -2998 3399 -3004 3195 -3194 3401 -2994 3403 -2994 3405 -3006 3193 -1598 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3395 -3002 3401 -1396 1793 -2990 3403 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -3002 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1599 -1598 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1601 -1596 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1795 -1394 3397 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -8002 1987 -2168 4109 -2272 4115 -2502 3705 -2690 3651 -2834 3423 -3016 +RAW_Data: 3403 -3002 3401 -3002 3401 -3002 3395 -3002 3407 -2996 3395 -2994 3401 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -3000 3407 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 3401 -2994 1801 -1400 3403 -2994 3401 -3002 1795 -1394 1797 -1402 3601 -1036 421 -848 843 -1518 1659 -2912 3565 -2984 3385 -2994 3395 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -2994 3403 -2994 3401 -3002 3393 -3004 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1597 -1594 1795 -1402 1801 -1400 3401 -2996 3401 -1396 1797 -3002 3407 -1404 1597 -1592 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 3401 -2994 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 1801 -1402 3393 -3002 3401 -3002 1597 -1594 3389 -7770 2021 -2912 3517 -3074 3245 -3022 3423 -3008 3411 -2998 3393 -2994 3401 -2994 3403 -3002 3393 -3002 3407 -2998 3393 -2994 3401 -3008 3397 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3393 -1402 1801 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -3004 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 +RAW_Data: 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -3002 1795 -1394 3399 -3002 3401 -2998 3405 -2996 3599 -1948 429 -20132 735 -734 1509 -192 387 -196272 217 -93450 441 -8180 183 -495896 381 -952 379 -594116 183 -209314 209 -296722 651 -199272 381 -98996 441 From 8370367f31536a1ce97028e18c1d5ad0767038fb Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:56:31 -0500 Subject: [PATCH 49/75] added common lrs pager frequency to subghz read & analyzer --- assets/resources/subghz/assets/setting_user.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt index 173511f40..67e89dceb 100644 --- a/assets/resources/subghz/assets/setting_user.txt +++ b/assets/resources/subghz/assets/setting_user.txt @@ -51,6 +51,7 @@ Frequency: 438900000 Frequency: 440175000 Frequency: 446000000 Frequency: 464000000 +Frequency: 467750000 Frequency: 779000000 Frequency: 868350000 Frequency: 868400000 From 7bde578a368f198ae92eda136d7a6f36bf170fab Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 14:04:53 -0500 Subject: [PATCH 50/75] added pager bruteforce playlist for looping --- assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt diff --git a/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt b/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt new file mode 100644 index 000000000..5ea4e2df5 --- /dev/null +++ b/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt @@ -0,0 +1,2 @@ +# Pager Bruteforce Playlist +sub: /ext/subghz/Misc/Pager_Bruteforce.sub From d7ecc95de44d3b4d57a80cab045ba787807dc50b Mon Sep 17 00:00:00 2001 From: Victor Nikitchuk Date: Thu, 9 Feb 2023 06:40:04 +0300 Subject: [PATCH 51/75] Firmware fixes and improvements for flashing via blackmagic (#2321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: quen0n Co-authored-by: あく Co-authored-by: hedger --- scripts/fbt_tools/fbt_debugopts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index f4b021c20..9abe59893 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -47,6 +47,7 @@ def generate(env, **kw): "source ${FBT_DEBUG_DIR}/gdbinit", ], GDBOPTS_BLACKMAGIC=[ + "-q", "-ex", "monitor swdp_scan", "-ex", From 71871949ec6999ab71fff5d25be8fb18a17a8186 Mon Sep 17 00:00:00 2001 From: Patrick Cunningham Date: Wed, 8 Feb 2023 19:47:16 -0800 Subject: [PATCH 52/75] Picopass: show elite key used from dictionary (#2119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * show elite key used from dictionary * remove space so it fits on screen Co-authored-by: あく --- .../scenes/picopass_scene_read_card_success.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index 0d1cc78c3..d89a5d89b 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -69,6 +69,19 @@ void picopass_scene_read_card_success_on_enter(void* context) { furi_string_cat_printf(sio_str, "+SIO"); } + if(pacs->key) { + if(pacs->sio) { + furi_string_cat_printf(sio_str, " "); + } + furi_string_cat_printf(sio_str, "Key: "); + + uint8_t key[PICOPASS_BLOCK_LEN]; + memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(sio_str, "%02X", key[i]); + } + } + widget_add_button_element( widget, GuiButtonTypeLeft, From 163be139ebb7a56d495d97ca87b662dfcd8a0bb8 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 9 Feb 2023 08:48:06 +0400 Subject: [PATCH 53/75] SubGhz: add protocol BinRAW (binarization of data quantized by the minimum correlated duration) (#2322) * SubGhz: add protocol DataRAW (binarization of data quantized by the minimum correlated duration) * SubGhz: fix name history * SubGhz: add encoder Data_RAW protocol * SubGhz: decreasing the size of the LevelDuration structure * SubGhz: history, added check that there is free RAM * SubGhz: checking for free memory, support to pass without gap * SubGhz: add running average to average the result, auto cut noise at the end of a burst * SubGhz: support for repeating sequences * SubGhz: fix secplus_v2 decoder * SubGhz: bin_RAW fix add history * SubGhz: add debug * SubGhz: debug refactoring * FURI_LOG: add FURI_LOG_RAW_x formatted string output like printf * SubGhz: fix new FURI_LOG metod * FURI_LOG: fix unit test * SubGhz: add enable/disable BinRAW protocol decoding * SubGhz: fix PVS * SubGhz: forcibly turn off the speaker when exiting SubGhz * SubGhz: adaptive adjustment to the noise level Co-authored-by: Aleksandr Kutuzov --- applications/debug/unit_tests/test_index.c | 2 +- applications/main/subghz/application.fam | 2 +- .../subghz/scenes/subghz_scene_read_raw.c | 2 +- .../subghz/scenes/subghz_scene_receiver.c | 13 + .../scenes/subghz_scene_receiver_config.c | 32 + applications/main/subghz/subghz.c | 5 +- applications/main/subghz/subghz_history.c | 40 +- applications/main/subghz/subghz_i.h | 1 + applications/main/subghz/views/receiver.c | 33 +- applications/main/subghz/views/receiver.h | 2 + .../subghz/views/subghz_frequency_analyzer.c | 1 - applications/main/subghz/views/transmitter.c | 7 +- firmware/targets/f7/api_symbols.csv | 3 +- furi/core/log.c | 31 +- furi/core/log.h | 51 +- lib/subghz/blocks/encoder.c | 23 +- lib/subghz/blocks/encoder.h | 11 +- lib/subghz/blocks/generic.c | 2 +- lib/subghz/blocks/generic.h | 2 +- lib/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ lib/subghz/protocols/bin_raw.h | 111 ++ lib/subghz/protocols/chamberlain_code.c | 5 +- lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + lib/subghz/protocols/secplus_v2.c | 8 +- lib/subghz/receiver.c | 2 +- lib/subghz/types.h | 1 + lib/toolbox/level_duration.h | 4 +- 28 files changed, 1451 insertions(+), 65 deletions(-) create mode 100644 lib/subghz/protocols/bin_raw.c create mode 100644 lib/subghz/protocols/bin_raw.h diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 2bb9c423f..ac71ca397 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -70,7 +70,7 @@ void minunit_print_progress() { } void minunit_print_fail(const char* str) { - printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str); + printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); } void unit_tests_cli(Cli* cli, FuriString* args, void* context) { diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 6df4b1a8a..f0dc66e89 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -12,7 +12,7 @@ App( ], provides=["subghz_start"], icon="A_Sub1ghz_14", - stack_size=2 * 1024, + stack_size=3 * 1024, order=10, ) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 6f95c4169..96acc90ee 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -411,5 +411,5 @@ void subghz_scene_read_raw_on_exit(void* context) { notification_message(subghz->notifications, &sequence_reset_rgb); //filter restoration - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 2b01e2975..93c369092 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/receiver.h" #include +#include static const NotificationSequence subghs_sequence_rx = { &message_green_255, @@ -143,6 +144,11 @@ void subghz_scene_receiver_on_enter(void* context) { } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); + //to use a universal decoder, we are looking for a link to it + subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( + subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); + furi_assert(subghz->txrx->decoder_result); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } @@ -208,6 +214,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_hopper_update(subghz); subghz_scene_receiver_update_statusbar(subghz); } + + //get RSSI + float rssi = furi_hal_subghz_get_rssi(); + subghz_receiver_rssi(subghz->subghz_receiver, rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); + switch(subghz->state_notifications) { case SubGhzNotificationStateRx: notification_message(subghz->notifications, &sequence_blink_cyan_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index b49aac922..895e43342 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,6 +5,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexBinRAW, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, @@ -58,6 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = { SubGhzSpeakerStateShutdown, SubGhzSpeakerStateEnable, }; +#define BIN_RAW_COUNT 2 +const char* const bin_raw_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t bin_raw_value[BIN_RAW_COUNT] = { + SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, +}; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); @@ -186,6 +196,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { subghz->txrx->speaker_state = speaker_value[index]; } +static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, bin_raw_text[index]); + subghz->txrx->filter = bin_raw_value[index]; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -254,6 +273,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text( item, subghz_setting_get_preset_name(subghz->setting, value_index)); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Bin_RAW:", + BIN_RAW_COUNT, + subghz_scene_receiver_config_set_bin_raw, + subghz); + value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, bin_raw_text[value_index]); + } + item = variable_item_list_add( subghz->variable_item_list, "Sound:", diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 200be6262..25233fe21 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -194,7 +194,8 @@ SubGhz* subghz_alloc() { subghz_environment_set_protocol_registry( subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz->txrx->filter = SubGhzProtocolFlag_Decodable; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_worker_set_overrun_callback( subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); @@ -218,6 +219,8 @@ void subghz_free(SubGhz* subghz) { subghz->rpc_ctx = NULL; } + subghz_speaker_off(subghz); + // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); subghz_test_packet_free(subghz->subghz_test_packet); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index beecc6e8b..e6c93e05c 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -5,6 +5,7 @@ #include #define SUBGHZ_HISTORY_MAX 50 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" typedef struct { @@ -121,8 +122,12 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx } bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Free heap LOW"); + return true; + } if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, "Memory is FULL"); + if(output != NULL) furi_string_printf(output, " Memory is FULL"); return true; } if(output != NULL) @@ -142,6 +147,7 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; @@ -200,27 +206,31 @@ bool subghz_history_add_to_history( } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; + FURI_LOG_D(TAG, "No Key"); } uint64_t data = 0; for(uint8_t i = 0; i < sizeof(uint64_t); i++) { data = (data << 8) | key_data[i]; } - if(!(uint32_t)(data >> 32)) { - furi_string_printf( - item->item_str, - "%s %lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); + if(data != 0) { + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } } else { - furi_string_printf( - item->item_str, - "%s %lX%08lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); + furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string)); } + } while(false); furi_string_free(text); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index cd33da447..65480c6fd 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -45,6 +45,7 @@ struct SubGhzTxRx { SubGhzEnvironment* environment; SubGhzReceiver* receiver; SubGhzTransmitter* transmitter; + SubGhzProtocolFlag filter; SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index aaec2adda..acc39e258 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -12,6 +12,8 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct { FuriString* item_str; uint8_t type; @@ -59,8 +61,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; SubGhzViewReceiverBarShow bar_show; + uint8_t u_rssi; } SubGhzViewReceiverModel; +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzViewReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -168,13 +186,22 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -206,11 +233,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(model->history_item == 0) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); + canvas_draw_str(canvas, 63, 44, "Scanning..."); canvas_set_font(canvas, FontSecondary); } + subghz_view_rssi_draw(canvas, model); switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index aab7a76c5..9b12ccfee 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -8,6 +8,8 @@ typedef struct SubGhzViewReceiver SubGhzViewReceiver; typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context); +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_callback( diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 94419084b..325664f4a 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -79,7 +79,6 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { uint8_t column_height = 6; if(rssi) { - //rssi = rssi if(rssi > 54) rssi = 54; for(uint8_t i = 1; i < rssi; i++) { if(i % 5) { diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 833805ccb..4a13460a3 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -84,9 +84,10 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 0, 8, furi_string_get_cstr(model->key_str)); - canvas_draw_str(canvas, 78, 8, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 113, 8, furi_string_get_cstr(model->preset_str)); + elements_multiline_text_aligned( + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); + canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 846041c3e..906ab357a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1409,6 +1409,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -2599,7 +2600,7 @@ Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8 Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" -Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" diff --git a/furi/core/log.c b/furi/core/log.c index a3967ed92..d910ecf21 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -28,27 +28,27 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form FuriString* string; string = furi_string_alloc(); - const char* color = FURI_LOG_CLR_RESET; + const char* color = _FURI_LOG_CLR_RESET; const char* log_letter = " "; switch(level) { case FuriLogLevelError: - color = FURI_LOG_CLR_E; + color = _FURI_LOG_CLR_E; log_letter = "E"; break; case FuriLogLevelWarn: - color = FURI_LOG_CLR_W; + color = _FURI_LOG_CLR_W; log_letter = "W"; break; case FuriLogLevelInfo: - color = FURI_LOG_CLR_I; + color = _FURI_LOG_CLR_I; log_letter = "I"; break; case FuriLogLevelDebug: - color = FURI_LOG_CLR_D; + color = _FURI_LOG_CLR_D; log_letter = "D"; break; case FuriLogLevelTrace: - color = FURI_LOG_CLR_T; + color = _FURI_LOG_CLR_T; log_letter = "T"; break; default: @@ -58,7 +58,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( string, - "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, + "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_log.timestamp(), color, log_letter, @@ -80,6 +80,23 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form } } +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { + if(level <= furi_log.log_level && + furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { + FuriString* string; + string = furi_string_alloc(); + va_list args; + va_start(args, format); + furi_string_vprintf(string, format, args); + va_end(args); + + furi_log.puts(furi_string_get_cstr(string)); + furi_string_free(string); + + furi_mutex_release(furi_log.mutex); + } +} + void furi_log_set_level(FuriLogLevel level) { if(level == FuriLogLevelDefault) { level = FURI_LOG_LEVEL_DEFAULT; diff --git a/furi/core/log.h b/furi/core/log.h index cb8b3d9cc..46ae7f007 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -22,21 +22,21 @@ typedef enum { FuriLogLevelTrace = 6, } FuriLogLevel; -#define FURI_LOG_CLR(clr) "\033[0;" clr "m" -#define FURI_LOG_CLR_RESET "\033[0m" +#define _FURI_LOG_CLR(clr) "\033[0;" clr "m" +#define _FURI_LOG_CLR_RESET "\033[0m" -#define FURI_LOG_CLR_BLACK "30" -#define FURI_LOG_CLR_RED "31" -#define FURI_LOG_CLR_GREEN "32" -#define FURI_LOG_CLR_BROWN "33" -#define FURI_LOG_CLR_BLUE "34" -#define FURI_LOG_CLR_PURPLE "35" +#define _FURI_LOG_CLR_BLACK "30" +#define _FURI_LOG_CLR_RED "31" +#define _FURI_LOG_CLR_GREEN "32" +#define _FURI_LOG_CLR_BROWN "33" +#define _FURI_LOG_CLR_BLUE "34" +#define _FURI_LOG_CLR_PURPLE "35" -#define FURI_LOG_CLR_E FURI_LOG_CLR(FURI_LOG_CLR_RED) -#define FURI_LOG_CLR_W FURI_LOG_CLR(FURI_LOG_CLR_BROWN) -#define FURI_LOG_CLR_I FURI_LOG_CLR(FURI_LOG_CLR_GREEN) -#define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE) -#define FURI_LOG_CLR_T FURI_LOG_CLR(FURI_LOG_CLR_PURPLE) +#define _FURI_LOG_CLR_E _FURI_LOG_CLR(_FURI_LOG_CLR_RED) +#define _FURI_LOG_CLR_W _FURI_LOG_CLR(_FURI_LOG_CLR_BROWN) +#define _FURI_LOG_CLR_I _FURI_LOG_CLR(_FURI_LOG_CLR_GREEN) +#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) +#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) typedef void (*FuriLogPuts)(const char* data); typedef uint32_t (*FuriLogTimestamp)(void); @@ -54,6 +54,15 @@ void furi_log_init(); void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 3, 4))); +/** Print log record + * + * @param level + * @param format + * @param ... + */ +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + /** Set log level * * @param[in] level The level @@ -95,6 +104,22 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp); #define FURI_LOG_T(tag, format, ...) \ furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) +/** Log methods + * + * @param format The raw format + * @param ... VA Args + */ +#define FURI_LOG_RAW_E(format, ...) \ + furi_log_print_raw_format(FuriLogLevelError, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_W(format, ...) \ + furi_log_print_raw_format(FuriLogLevelWarn, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_I(format, ...) \ + furi_log_print_raw_format(FuriLogLevelInfo, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_D(format, ...) \ + furi_log_print_raw_format(FuriLogLevelDebug, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_T(format, ...) \ + furi_log_print_raw_format(FuriLogLevelTrace, format, ##__VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index f3349b5fc..49ec4f177 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -2,6 +2,8 @@ #include "math.h" #include +#include "furi.h" + #define TAG "SubGhzBlockEncoder" void subghz_protocol_blocks_set_bit_array( @@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); } -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit) { - size_t index_bit = 0; + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; size_t size_upload = 0; uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1; i < count_bit_data_array; i++) { + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { duration += duration_bit; } else { - furi_assert(max_size_upload > size_upload); + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); last_bit = !last_bit; diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 1ff077726..aeaa2add0 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -19,6 +19,11 @@ typedef struct { } SubGhzProtocolBlockEncoder; +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + /** * Set data bit when encoding HEX array. * @param bit_value The value of the bit to be set @@ -47,13 +52,15 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde * @param upload Pointer to a LevelDuration * @param max_size_upload upload size, check not to overflow * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array */ -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit); + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); #ifdef __cplusplus } diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 94114676d..3d59adc82 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -100,7 +100,7 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma FURI_LOG_E(TAG, "Missing Bit"); break; } - instance->data_count_bit = (uint8_t)temp_data; + instance->data_count_bit = (uint16_t)temp_data; uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index d1c7dc356..284df51ae 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -19,7 +19,7 @@ struct SubGhzBlockGeneric { const char* protocol_name; uint64_t data; uint32_t serial; - uint8_t data_count_bit; + uint16_t data_count_bit; uint8_t btn; uint32_t cnt; }; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..c3f544110 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeStatic, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 32f4e9520..9c8e5ee4a 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -196,12 +196,13 @@ static bool break; } - instance->encoder.size_upload = subghz_protocol_blocks_get_upload( + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( upload_hex_data, upload_hex_count_bit, instance->encoder.upload, instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short); + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); return true; } diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 050904eec..74244c5ff 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -42,6 +42,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 522931d22..4ca1c4679 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -42,5 +42,6 @@ #include "dooya.h" #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" +#include "bin_raw.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 7b79892b0..f7262bd17 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -261,16 +261,16 @@ static bool data = order << 4 | invert; int k = 0; for(int i = 6; i >= 0; i -= 2) { - roll_array[k++] = (data >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } } for(int i = 8; i >= 0; i -= 2) { - roll_array[k++] = (p[2] >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index fd6f1493e..698fe098e 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -64,7 +64,7 @@ void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t durat for M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - if((slot->base->protocol->flag & instance->filter) == instance->filter) { + if((slot->base->protocol->flag & instance->filter) != 0) { slot->base->protocol->decoder->feed(slot->base, level, duration); } } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 6c34dc728..1b8ef6a14 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -90,6 +90,7 @@ typedef enum { SubGhzProtocolFlag_Save = (1 << 7), SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; typedef struct { diff --git a/lib/toolbox/level_duration.h b/lib/toolbox/level_duration.h index 9406cef31..7618344ac 100644 --- a/lib/toolbox/level_duration.h +++ b/lib/toolbox/level_duration.h @@ -13,8 +13,8 @@ #define LEVEL_DURATION_RESERVED 0x800000U typedef struct { - uint32_t level; - uint32_t duration; + uint32_t duration : 30; + uint8_t level : 2; } LevelDuration; static inline LevelDuration level_duration_make(bool level, uint32_t duration) { From 4265057ee864a8d68a68e9a9ef4088b39d2b1e5c Mon Sep 17 00:00:00 2001 From: Petr Portnov | PROgrm_JARvis Date: Thu, 9 Feb 2023 07:58:01 +0300 Subject: [PATCH 54/75] feat: add missing `const` qualifiers (#2233) * feat: make `ViewPort` getters const * feat: make tx-buffers const * feat: make `canvas_get_buffer_size` const * feat: make `canvas` methods const * feat: make `icon_animation` methods const * feat: make `scene_manager` methods const * feat: make `loader` method const * feat: make `canvas_get_font_params` const Co-authored-by: Aleksandr Kutuzov --- applications/services/gui/canvas.c | 12 +++--- applications/services/gui/canvas.h | 8 ++-- applications/services/gui/canvas_i.h | 2 +- applications/services/gui/elements.c | 2 +- applications/services/gui/gui.c | 2 +- applications/services/gui/gui.h | 2 +- applications/services/gui/icon_animation.c | 8 ++-- applications/services/gui/icon_animation.h | 6 +-- applications/services/gui/icon_animation_i.h | 2 +- .../widget_element_text_scroll.c | 4 +- applications/services/gui/scene_manager.c | 4 +- applications/services/gui/scene_manager.h | 4 +- applications/services/gui/view_port.c | 6 +-- applications/services/gui/view_port.h | 6 +-- applications/services/loader/loader.c | 2 +- applications/services/loader/loader.h | 2 +- applications/services/locale/locale.h | 2 +- firmware/targets/f18/api_symbols.csv | 35 ++++++++--------- firmware/targets/f7/api_symbols.csv | 38 +++++++++---------- firmware/targets/f7/furi_hal/furi_hal_spi.c | 4 +- .../targets/furi_hal_include/furi_hal_spi.h | 4 +- 21 files changed, 78 insertions(+), 77 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 18636eced..d47bba6b2 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -57,7 +57,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas) { return u8g2_GetBufferPtr(&canvas->fb); } -size_t canvas_get_buffer_size(Canvas* canvas) { +size_t canvas_get_buffer_size(const Canvas* canvas) { furi_assert(canvas); return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8; } @@ -75,17 +75,17 @@ void canvas_frame_set( canvas->height = height; } -uint8_t canvas_width(Canvas* canvas) { +uint8_t canvas_width(const Canvas* canvas) { furi_assert(canvas); return canvas->width; } -uint8_t canvas_height(Canvas* canvas) { +uint8_t canvas_height(const Canvas* canvas) { furi_assert(canvas); return canvas->height; } -uint8_t canvas_current_font_height(Canvas* canvas) { +uint8_t canvas_current_font_height(const Canvas* canvas) { furi_assert(canvas); uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb); @@ -96,10 +96,10 @@ uint8_t canvas_current_font_height(Canvas* canvas) { return font_height; } -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) { +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) { furi_assert(canvas); furi_assert(font < FontTotalNumber); - return (CanvasFontParameters*)&canvas_font_params[font]; + return &canvas_font_params[font]; } void canvas_clear(Canvas* canvas) { diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index b2a065ca7..f8fe2c1db 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -85,7 +85,7 @@ void canvas_commit(Canvas* canvas); * * @return width in pixels. */ -uint8_t canvas_width(Canvas* canvas); +uint8_t canvas_width(const Canvas* canvas); /** Get Canvas height * @@ -93,7 +93,7 @@ uint8_t canvas_width(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_height(Canvas* canvas); +uint8_t canvas_height(const Canvas* canvas); /** Get current font height * @@ -101,7 +101,7 @@ uint8_t canvas_height(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_current_font_height(Canvas* canvas); +uint8_t canvas_current_font_height(const Canvas* canvas); /** Get font parameters * @@ -110,7 +110,7 @@ uint8_t canvas_current_font_height(Canvas* canvas); * * @return pointer to CanvasFontParameters structure */ -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font); +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font); /** Clear canvas * diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 2b6849235..12cabfa7d 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -45,7 +45,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas); * * @return size of canvas in bytes */ -size_t canvas_get_buffer_size(Canvas* canvas); +size_t canvas_get_buffer_size(const Canvas* canvas); /** Set drawing region relative to real screen buffer * diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 54c36af76..9b7c84ece 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -639,7 +639,7 @@ void elements_text_box( bool inversed_present = false; Font current_font = FontSecondary; Font prev_font = FontSecondary; - CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); + const CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); // Fill line parameters uint8_t line_leading_min = font_params->leading_min; diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index af5cf862d..94bab1402 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -467,7 +467,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, gui_unlock(gui); } -size_t gui_get_framebuffer_size(Gui* gui) { +size_t gui_get_framebuffer_size(const Gui* gui) { furi_assert(gui); return canvas_get_buffer_size(gui->canvas); } diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index 4e7fc2e4d..d7d73f27b 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -94,7 +94,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, * @param gui Gui instance * @return size_t size of frame buffer in bytes */ -size_t gui_get_framebuffer_size(Gui* gui); +size_t gui_get_framebuffer_size(const Gui* gui); /** Set lockdown mode * diff --git a/applications/services/gui/icon_animation.c b/applications/services/gui/icon_animation.c index 48c862208..b63d233f3 100644 --- a/applications/services/gui/icon_animation.c +++ b/applications/services/gui/icon_animation.c @@ -29,7 +29,7 @@ void icon_animation_set_update_callback( instance->callback_context = context; } -const uint8_t* icon_animation_get_data(IconAnimation* instance) { +const uint8_t* icon_animation_get_data(const IconAnimation* instance) { return instance->icon->frames[instance->frame]; } @@ -51,12 +51,12 @@ void icon_animation_timer_callback(void* context) { } } -uint8_t icon_animation_get_width(IconAnimation* instance) { +uint8_t icon_animation_get_width(const IconAnimation* instance) { furi_assert(instance); return instance->icon->width; } -uint8_t icon_animation_get_height(IconAnimation* instance) { +uint8_t icon_animation_get_height(const IconAnimation* instance) { furi_assert(instance); return instance->icon->height; } @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance) { } } -bool icon_animation_is_last_frame(IconAnimation* instance) { +bool icon_animation_is_last_frame(const IconAnimation* instance) { furi_assert(instance); return instance->icon->frame_count - instance->frame <= 1; } diff --git a/applications/services/gui/icon_animation.h b/applications/services/gui/icon_animation.h index 684790353..6e58df31d 100644 --- a/applications/services/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -55,7 +55,7 @@ void icon_animation_set_update_callback( * * @return width in pixels */ -uint8_t icon_animation_get_width(IconAnimation* instance); +uint8_t icon_animation_get_width(const IconAnimation* instance); /** Get icon animation height * @@ -63,7 +63,7 @@ uint8_t icon_animation_get_width(IconAnimation* instance); * * @return height in pixels */ -uint8_t icon_animation_get_height(IconAnimation* instance); +uint8_t icon_animation_get_height(const IconAnimation* instance); /** Start icon animation * @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance); * * @return true if last frame */ -bool icon_animation_is_last_frame(IconAnimation* instance); +bool icon_animation_is_last_frame(const IconAnimation* instance); #ifdef __cplusplus } diff --git a/applications/services/gui/icon_animation_i.h b/applications/services/gui/icon_animation_i.h index 4053a120d..62858d288 100644 --- a/applications/services/gui/icon_animation_i.h +++ b/applications/services/gui/icon_animation_i.h @@ -24,7 +24,7 @@ struct IconAnimation { * * @return pointer to current frame XBM bitmap data */ -const uint8_t* icon_animation_get_data(IconAnimation* instance); +const uint8_t* icon_animation_get_data(const IconAnimation* instance); /** Advance to next frame * diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index d8fc11311..5d522c74b 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -74,7 +74,7 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* } // Set canvas font canvas_set_font(canvas, line_tmp.font); - CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); total_height += params->height; if(total_height > model->height) { model->scroll_pos_total++; @@ -138,7 +138,7 @@ static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* eleme TextScrollLineArray_next(it), curr_line++) { if(curr_line < model->scroll_pos_current) continue; TextScrollLineArray* line = TextScrollLineArray_ref(it); - CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); if(y + params->descender > model->y + model->height) break; canvas_set_font(canvas, line->font); if(line->horizontal == AlignLeft) { diff --git a/applications/services/gui/scene_manager.c b/applications/services/gui/scene_manager.c index 590145e1e..4064dafb6 100644 --- a/applications/services/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -34,7 +34,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i scene_manager->scene[scene_id].state = state; } -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id) { +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); furi_assert(scene_id < scene_manager->scene_handlers->scene_num); @@ -184,7 +184,7 @@ bool scene_manager_search_and_switch_to_previous_scene_one_of( return scene_found; } -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); bool scene_found = false; diff --git a/applications/services/gui/scene_manager.h b/applications/services/gui/scene_manager.h index 5b833e5de..c349a12ce 100644 --- a/applications/services/gui/scene_manager.h +++ b/applications/services/gui/scene_manager.h @@ -63,7 +63,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i * * @return Scene state */ -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id); +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id); /** Scene Manager allocation and configuration * @@ -134,7 +134,7 @@ bool scene_manager_previous_scene(SceneManager* scene_manager); * * @return true if previous scene was found, false otherwise */ -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id); +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id); /** Search and switch to previous Scene * diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index ffd01450b..5760ed577 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -89,7 +89,7 @@ void view_port_set_width(ViewPort* view_port, uint8_t width) { view_port->width = width; } -uint8_t view_port_get_width(ViewPort* view_port) { +uint8_t view_port_get_width(const ViewPort* view_port) { furi_assert(view_port); return view_port->width; } @@ -99,7 +99,7 @@ void view_port_set_height(ViewPort* view_port, uint8_t height) { view_port->height = height; } -uint8_t view_port_get_height(ViewPort* view_port) { +uint8_t view_port_get_height(const ViewPort* view_port) { furi_assert(view_port); return view_port->height; } @@ -112,7 +112,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) { } } -bool view_port_is_enabled(ViewPort* view_port) { +bool view_port_is_enabled(const ViewPort* view_port) { furi_assert(view_port); return view_port->is_enabled; } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 703e99248..752fc46ba 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -56,7 +56,7 @@ void view_port_free(ViewPort* view_port); * @param width wanted width, 0 - auto. */ void view_port_set_width(ViewPort* view_port, uint8_t width); -uint8_t view_port_get_width(ViewPort* view_port); +uint8_t view_port_get_width(const ViewPort* view_port); /** Set view_port height. * @@ -66,7 +66,7 @@ uint8_t view_port_get_width(ViewPort* view_port); * @param height wanted height, 0 - auto. */ void view_port_set_height(ViewPort* view_port, uint8_t height); -uint8_t view_port_get_height(ViewPort* view_port); +uint8_t view_port_get_height(const ViewPort* view_port); /** Enable or disable view_port rendering. * @@ -75,7 +75,7 @@ uint8_t view_port_get_height(ViewPort* view_port); * @warning automatically dispatches update event */ void view_port_enabled_set(ViewPort* view_port, bool enabled); -bool view_port_is_enabled(ViewPort* view_port); +bool view_port_is_enabled(const ViewPort* view_port); /** ViewPort event callbacks * diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 97d1e6e4e..caaf9f11b 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -262,7 +262,7 @@ void loader_unlock(Loader* instance) { FURI_CRITICAL_EXIT(); } -bool loader_is_locked(Loader* instance) { +bool loader_is_locked(const Loader* instance) { return instance->lock_count > 0; } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 954e79c57..8dbc4fc35 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -43,7 +43,7 @@ bool loader_lock(Loader* instance); void loader_unlock(Loader* instance); /** Get loader lock status */ -bool loader_is_locked(Loader* instance); +bool loader_is_locked(const Loader* instance); /** Show primary loader */ void loader_show_menu(); diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h index 5f2a4d87f..61fb4c605 100644 --- a/applications/services/locale/locale.h +++ b/applications/services/locale/locale.h @@ -81,7 +81,7 @@ void locale_format_time( * * @return The Locale DateFormat. */ -LocaleDateFormat locale_get_date_format(); +LocaleDateFormat locale_get_date_format(void); /** Set Locale DateFormat * diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index d29d696fc..462fbf739 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.1,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -522,7 +522,7 @@ Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* Function,+,canvas_commit,void,Canvas* -Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_current_font_height,uint8_t,const Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -539,9 +539,9 @@ Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const Canvas* Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" @@ -550,7 +550,7 @@ Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* @@ -1063,9 +1063,9 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -1123,6 +1123,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -1287,7 +1288,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1301,9 +1302,9 @@ Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1352,7 +1353,7 @@ Function,-,ldiv,ldiv_t,"long, long" Function,-,llabs,long long,long long Function,-,lldiv,lldiv_t,"long long, long long" Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -1570,11 +1571,11 @@ Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -1946,11 +1947,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 906ab357a..33c443ae0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.1,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -607,7 +607,7 @@ Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* Function,+,canvas_commit,void,Canvas* -Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_current_font_height,uint8_t,const Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -624,18 +624,18 @@ Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const Canvas* Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" -Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" +Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float Function,-,cbrtl,long double,long double @@ -1095,7 +1095,6 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* -Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1249,6 +1248,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1319,9 +1319,9 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -1578,7 +1578,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1621,9 +1621,9 @@ Function,+,ibutton_worker_write_set_callback,void,"iButtonWorker*, iButtonWorker Function,+,ibutton_worker_write_start,void,"iButtonWorker*, iButtonKey*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1763,7 +1763,7 @@ Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -2372,11 +2372,11 @@ Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -2895,11 +2895,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 8dba8327f..42b854799 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -103,7 +103,7 @@ bool furi_hal_spi_bus_rx( bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout) { furi_assert(handle); @@ -128,7 +128,7 @@ bool furi_hal_spi_bus_tx( bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) { diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index 79e809317..af15a8838 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -85,7 +85,7 @@ bool furi_hal_spi_bus_rx( */ bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout); @@ -101,7 +101,7 @@ bool furi_hal_spi_bus_tx( */ bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout); From 82c730b6bec8095fea3aef1aaf2014212b90410a Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:45:30 +0400 Subject: [PATCH 55/75] SubGhz: fix cc1101_read_fifo func (#2379) --- lib/drivers/cc1101.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index d563c30c3..d0feb0218 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -150,9 +150,8 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint } uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) { - uint8_t buff_tx[64]; - buff_tx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; - uint8_t buff_rx[2]; + uint8_t buff_trx[2]; + buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; // Start transaction // Wait IC to become ready @@ -160,15 +159,15 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si ; // First byte - packet length - furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, buff_trx, buff_trx, 2, CC1101_TIMEOUT); // Check that the packet is placed in the receive buffer - if(buff_rx[1] > 64) { + if(buff_trx[1] > 64) { *size = 64; } else { - *size = buff_rx[1]; + *size = buff_trx[1]; } - furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, NULL, data, *size, CC1101_TIMEOUT); return *size; -} +} \ No newline at end of file From 67c2d1cf6144da79e0077427b6ca09ee85f730ea Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Thu, 9 Feb 2023 13:42:41 +0300 Subject: [PATCH 56/75] Migrating CI/CD to Linode S3 (#2380) * Test PVS linode S3 * Migrating to Linode S3 * Disable PVS action debug * Fix pvs_studio.yml --- .github/workflows/build.yml | 11 +++++------ .github/workflows/pvs_studio.yml | 21 ++++++++++----------- scripts/merge_report_qa.py | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be9817684..689dd2037 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,14 +96,14 @@ jobs: - name: 'Upload map analyser files to storage' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: keithweaver/aws-s3-github-action@v1.0.0 + uses: prewk/s3-cp-action@v2 with: - source: map_analyser_files/ - destination: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" + aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" - aws_region: "${{ secrets.MAP_REPORT_AWS_REGION }}" - flags: --recursive + source: "./map_analyser_files/" + dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" + flags: "--recursive --acl public-read" - name: 'Trigger map file reporter' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -114,7 +114,6 @@ jobs: event-type: map-file-analyse client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' - - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} run: | diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index a4ac6e301..65a8b6150 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -54,17 +54,16 @@ jobs: ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - name: 'Upload artifacts to update server' + - name: 'Upload report' if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - run: | - mkdir -p ~/.ssh - ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avrzP --mkpath \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - build/f7-firmware-DC/pvsreport/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/"; - rm ./deploy_key; + uses: prewk/s3-cp-action@v2 + with: + aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" + aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" + aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" + source: "./build/f7-firmware-DC/pvsreport" + dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" + flags: "--recursive --acl public-read" - name: 'Find Previous Comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} @@ -83,7 +82,7 @@ jobs: issue-number: ${{ github.event.pull_request.number }} body: | **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) + - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) edit-mode: replace - name: 'Raise exception' diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py index c0707848e..caa742408 100755 --- a/scripts/merge_report_qa.py +++ b/scripts/merge_report_qa.py @@ -17,7 +17,7 @@ def parse_args(): def checkCommitMessage(msg): - regex = re.compile(r"^'?\[FL-\d+\]") + regex = re.compile(r"^'?\[(FL-\d+,?\s?)+\]") if regex.match(msg): return True return False From af869ef8d2f62d878a8571f29e7bfb2230aa6db7 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Thu, 9 Feb 2023 18:34:56 +0100 Subject: [PATCH 57/75] Merging OFW | Suffering | Part 1 | Wont build as is I honestly dont even know anymore... --- .github/workflows/pvs_studio.yml | 93 ++ .../debug/example_custom_font/application.fam | 9 + .../example_custom_font/example_custom_font.c | 98 ++ .../examples/example_thermo/README.md | 44 + .../examples/example_thermo/application.fam | 10 + .../examples/example_thermo/example_thermo.c | 356 ++++++ .../example_thermo/example_thermo_10px.png | Bin 0 -> 7293 bytes .../resources/badusb/assets/layouts/ba-BA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/cz_CS.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/da-DA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-DE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/dvorak.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-UK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-US.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/es-ES.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-BE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-FR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hr-HR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hu-HU.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/it-IT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nb-NO.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nl-NL.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-BR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-PT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/si-SI.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sk-SK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sv-SE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/tr-TR.kl | Bin 0 -> 256 bytes assets/resources/infrared/assets/projector.ir | 829 ++++++++++++ assets/resources/subghz/assets/alutech_at_4n | 6 + .../unit_tests/subghz/alutech_at_4n_raw.sub | 10 + assets/unit_tests/subghz/dooya.sub | 7 + assets/unit_tests/subghz/dooya_raw.sub | 8 + .../subghz/kinggates_stylo4k_raw.sub | 11 + assets/unit_tests/subghz/linear_delta3.sub | 7 + .../unit_tests/subghz/linear_delta3_raw.sub | 8 + assets/unit_tests/subghz/nice_one_raw.sub | 12 + documentation/UniversalRemotes.md | 76 ++ firmware/targets/f7/api_symbols.csv | 75 +- firmware/targets/f7/fatfs/sd_spi_io.c | 877 +++++++++++++ firmware/targets/f7/fatfs/sd_spi_io.h | 157 +++ firmware/targets/f7/furi_hal/furi_hal_spi.c | 235 +++- .../targets/furi_hal_include/furi_hal_spi.h | 25 +- lib/subghz/blocks/encoder.c | 25 +- lib/subghz/blocks/encoder.h | 11 +- lib/subghz/environment.c | 16 + lib/subghz/environment.h | 17 + lib/subghz/protocols/alutech_at_4n.c | 455 +++++++ lib/subghz/protocols/alutech_at_4n.h | 74 ++ lib/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ lib/subghz/protocols/bin_raw.h | 111 ++ lib/subghz/protocols/chamberlain_code.c | 5 +- lib/subghz/protocols/dooya.c | 447 +++++++ lib/subghz/protocols/dooya.h | 107 ++ lib/subghz/protocols/keeloq.c | 13 +- lib/subghz/protocols/kinggates_stylo_4k.c | 336 +++++ lib/subghz/protocols/kinggates_stylo_4k.h | 74 ++ lib/subghz/protocols/linear_delta3.c | 359 ++++++ lib/subghz/protocols/linear_delta3.h | 111 ++ lib/subghz/protocols/nice_flor_s.c | 167 ++- lib/subghz/protocols/protocol_items.c | 57 +- lib/subghz/protocols/protocol_items.h | 4 + lib/subghz/protocols/secplus_v1.c | 4 +- lib/subghz/protocols/somfy_keytis.c | 366 +++++- lib/subghz/protocols/somfy_keytis.h | 52 + lib/subghz/protocols/somfy_telis.c | 298 ++++- lib/subghz/protocols/somfy_telis.h | 52 + lib/subghz/subghz_setting.c | 5 +- lib/subghz/subghz_tx_rx_worker.c | 10 +- lib/subghz/types.h | 1 + 72 files changed, 7144 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/pvs_studio.yml create mode 100644 applications/debug/example_custom_font/application.fam create mode 100644 applications/debug/example_custom_font/example_custom_font.c create mode 100644 applications/examples/example_thermo/README.md create mode 100644 applications/examples/example_thermo/application.fam create mode 100644 applications/examples/example_thermo/example_thermo.c create mode 100644 applications/examples/example_thermo/example_thermo_10px.png create mode 100644 assets/resources/badusb/assets/layouts/ba-BA.kl create mode 100644 assets/resources/badusb/assets/layouts/cz_CS.kl create mode 100644 assets/resources/badusb/assets/layouts/da-DA.kl create mode 100644 assets/resources/badusb/assets/layouts/de-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/de-DE.kl create mode 100644 assets/resources/badusb/assets/layouts/dvorak.kl create mode 100644 assets/resources/badusb/assets/layouts/en-UK.kl create mode 100644 assets/resources/badusb/assets/layouts/en-US.kl create mode 100644 assets/resources/badusb/assets/layouts/es-ES.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-BE.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-FR.kl create mode 100644 assets/resources/badusb/assets/layouts/hr-HR.kl create mode 100644 assets/resources/badusb/assets/layouts/hu-HU.kl create mode 100644 assets/resources/badusb/assets/layouts/it-IT.kl create mode 100644 assets/resources/badusb/assets/layouts/nb-NO.kl create mode 100644 assets/resources/badusb/assets/layouts/nl-NL.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-BR.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-PT.kl create mode 100644 assets/resources/badusb/assets/layouts/si-SI.kl create mode 100644 assets/resources/badusb/assets/layouts/sk-SK.kl create mode 100644 assets/resources/badusb/assets/layouts/sv-SE.kl create mode 100644 assets/resources/badusb/assets/layouts/tr-TR.kl create mode 100644 assets/resources/infrared/assets/projector.ir create mode 100644 assets/resources/subghz/assets/alutech_at_4n create mode 100644 assets/unit_tests/subghz/alutech_at_4n_raw.sub create mode 100644 assets/unit_tests/subghz/dooya.sub create mode 100644 assets/unit_tests/subghz/dooya_raw.sub create mode 100644 assets/unit_tests/subghz/kinggates_stylo4k_raw.sub create mode 100644 assets/unit_tests/subghz/linear_delta3.sub create mode 100644 assets/unit_tests/subghz/linear_delta3_raw.sub create mode 100644 assets/unit_tests/subghz/nice_one_raw.sub create mode 100644 documentation/UniversalRemotes.md create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.c create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.h create mode 100644 lib/subghz/protocols/alutech_at_4n.c create mode 100644 lib/subghz/protocols/alutech_at_4n.h create mode 100644 lib/subghz/protocols/bin_raw.c create mode 100644 lib/subghz/protocols/bin_raw.h create mode 100644 lib/subghz/protocols/dooya.c create mode 100644 lib/subghz/protocols/dooya.h create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.c create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.h create mode 100644 lib/subghz/protocols/linear_delta3.c create mode 100644 lib/subghz/protocols/linear_delta3.h diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml new file mode 100644 index 000000000..65a8b6150 --- /dev/null +++ b/.github/workflows/pvs_studio.yml @@ -0,0 +1,93 @@ +name: 'Static C/C++ analysis with PVS-Studio' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + +jobs: + analyse_c_cpp: + if: ${{ !github.event.pull_request.head.repo.fork }} + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + id: names + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Supply PVS credentials' + run: | + pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} + + - name: 'Convert PVS-Studio output to html and detect warnings' + id: pvs-warn + run: | + WARNINGS=0 + ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 + echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT + + - name: 'Upload report' + if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} + uses: prewk/s3-cp-action@v2 + with: + aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" + aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" + aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" + source: "./build/f7-firmware-DC/pvsreport" + dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" + flags: "--recursive --acl public-read" + + - name: 'Find Previous Comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'PVS-Studio report for commit' + + - name: 'Create or update comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/create-or-update-comment@v1 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** + - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) + edit-mode: replace + + - name: 'Raise exception' + if: ${{ steps.pvs-warn.outputs.warnings != 0 }} + run: | + echo "Please fix all PVS warnings before merge" + exit 1 + diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam new file mode 100644 index 000000000..02285b8a0 --- /dev/null +++ b/applications/debug/example_custom_font/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_custom_font", + name="Example: custom font", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_custom_font_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c new file mode 100644 index 000000000..15eeb5f02 --- /dev/null +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +//This arrays contains the font itself. You can use any u8g2 font you want + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310 +#include + +#include +#include + +#include + +#include +#include + +#define UPDATE_PERIOD_MS 1000UL +#define TEXT_STORE_SIZE 64U + +#define DS18B20_CMD_CONVERT 0x44U +#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU + +#define DS18B20_CFG_RESOLUTION_POS 5U +#define DS18B20_CFG_RESOLUTION_MASK 0x03U +#define DS18B20_DECIMAL_PART_MASK 0x0fU + +#define DS18B20_SIGN_MASK 0xf0U + +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) + +/* Flags which the reader thread responds to */ +typedef enum { + ReaderThreadFlagExit = 1, +} ReaderThreadFlag; + +typedef union { + struct { + uint8_t temp_lsb; /* Least significant byte of the temperature */ + uint8_t temp_msb; /* Most significant byte of the temperature */ + uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ + uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ + uint8_t config; /* Configuration register */ + uint8_t reserved[3]; /* Not used */ + uint8_t crc; /* CRC checksum for error detection */ + } fields; + uint8_t bytes[9]; +} DS18B20Scratchpad; + +/* Application context structure */ +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriThread* reader_thread; + FuriMessageQueue* event_queue; + OneWireHost* onewire; + float temp_celsius; + bool has_device; +} ExampleThermoContext; + +/*************** 1-Wire Communication and Processing *****************/ + +/* Commands the thermometer to begin measuring the temperature. */ +static void example_thermo_request_temperature(ExampleThermoContext* context) { + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) break; + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + /* After the ROM operation, a device-specific command is issued. + In this case, it's a request to start measuring the temperature. */ + onewire_host_write(onewire, DS18B20_CMD_CONVERT); + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Reads the measured temperature from the thermometer. */ +static void example_thermo_read_temperature(ExampleThermoContext* context) { + /* If there was no device detected, don't try to read the temperature */ + if(!context->has_device) { + return; + } + + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + + do { + DS18B20Scratchpad buf; + + /* Attempt reading the temperature 10 times before giving up */ + size_t attempts_left = 10; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) continue; + + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + + /* After the ROM operation, a device-specific command is issued. + This time, it will be the "Read Scratchpad" command which will + prepare the device's internal buffer memory for reading. */ + onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); + + /* The actual reading happens here. A total of 9 bytes is read. */ + onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); + + /* Calculate the checksum and compare it with one provided by the device. */ + const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); + + /* Checksums match, exit the loop */ + if(crc == buf.fields.crc) break; + + } while(--attempts_left); + + if(attempts_left == 0) break; + + /* Get the measurement resolution from the configuration register. (See [1] page 9) */ + const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & + DS18B20_CFG_RESOLUTION_MASK; + + /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ + const uint8_t decimal_mask = + (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & + DS18B20_DECIMAL_PART_MASK; + + /* Get the integer and decimal part of the temperature (See [1] page 6) */ + const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); + const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; + + /* Calculate the sign of the temperature (See [1] page 6) */ + const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; + + /* Combine the integer and decimal part together */ + const float temp_celsius_abs = integer_part + decimal_part / 16.f; + + /* Set the appropriate sign */ + context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ +static int32_t example_thermo_reader_thread_callback(void* ctx) { + ExampleThermoContext* context = ctx; + + for(;;) { + /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ + example_thermo_request_temperature(context); + + /* Wait for the measurement to finish. At the same time wait for an exit signal. */ + const uint32_t flags = + furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); + + /* If an exit signal was received, return from this thread. */ + if(flags != (unsigned)FuriFlagErrorTimeout) break; + + /* The measurement is now ready, read it from the termometer. */ + example_thermo_read_temperature(context); + } + + return 0; +} + +/*************** GUI, Input and Main Loop *****************/ + +/* Draw the GUI of the application. The screen is completely redrawn during each call. */ +static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { + ExampleThermoContext* context = ctx; + char text_store[TEXT_STORE_SIZE]; + const size_t middle_x = canvas_width(canvas) / 2U; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); + canvas_draw_line(canvas, 0, 16, 128, 16); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer"); + + snprintf( + text_store, + TEXT_STORE_SIZE, + "to GPIO pin %ld", + furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); + canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); + + canvas_set_font(canvas, FontKeyboard); + + if(context->has_device) { + float temp; + char temp_units; + + /* The applicaton is locale-aware. + Change Settings->System->Units to check it out. */ + switch(locale_get_measurement_unit()) { + case LocaleMeasurementUnitsMetric: + temp = context->temp_celsius; + temp_units = 'C'; + break; + case LocaleMeasurementUnitsImperial: + temp = locale_celsius_to_fahrenheit(context->temp_celsius); + temp_units = 'F'; + break; + default: + furi_crash("Illegal measurement units"); + } + /* If a reading is available, display it */ + snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); + } else { + /* Or show a message that no data is available */ + strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + } + + canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); +} + +/* This function is called from the GUI thread. All it does is put the event + into the application's queue so it can be processed later. */ +static void example_thermo_input_callback(InputEvent* event, void* ctx) { + ExampleThermoContext* context = ctx; + furi_message_queue_put(context->event_queue, event, FuriWaitForever); +} + +/* Starts the reader thread and handles the input */ +static void example_thermo_run(ExampleThermoContext* context) { + /* Configure the hardware in host mode */ + onewire_host_start(context->onewire); + + /* Start the reader thread. It will talk to the thermometer in the background. */ + furi_thread_start(context->reader_thread); + + /* An endless loop which handles the input*/ + for(bool is_running = true; is_running;) { + InputEvent event; + /* Wait for an input event. Input events come from the GUI thread via a callback. */ + const FuriStatus status = + furi_message_queue_get(context->event_queue, &event, FuriWaitForever); + + /* This application is only interested in short button presses. */ + if((status != FuriStatusOk) || (event.type != InputTypeShort)) { + continue; + } + + /* When the user presses the "Back" button, break the loop and exit the application. */ + if(event.key == InputKeyBack) { + is_running = false; + } + } + + /* Signal the reader thread to cease operation and exit */ + furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); + + /* Wait for the reader thread to finish */ + furi_thread_join(context->reader_thread); + + /* Reset the hardware */ + onewire_host_stop(context->onewire); +} + +/******************** Initialisation & startup *****************************/ + +/* Allocate the memory and initialise the variables */ +static ExampleThermoContext* example_thermo_context_alloc() { + ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); + + context->view_port = view_port_alloc(); + view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); + view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); + + context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + context->reader_thread = furi_thread_alloc(); + furi_thread_set_stack_size(context->reader_thread, 1024U); + furi_thread_set_context(context->reader_thread, context); + furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); + + context->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); + + context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); + + return context; +} + +/* Release the unused resources and deallocate memory */ +static void example_thermo_context_free(ExampleThermoContext* context) { + view_port_enabled_set(context->view_port, false); + gui_remove_view_port(context->gui, context->view_port); + + onewire_host_free(context->onewire); + furi_thread_free(context->reader_thread); + furi_message_queue_free(context->event_queue); + view_port_free(context->view_port); + + furi_record_close(RECORD_GUI); +} + +/* The application's entry point. Execution starts from here. */ +int32_t example_thermo_main(void* p) { + UNUSED(p); + + /* Allocate all of the necessary structures */ + ExampleThermoContext* context = example_thermo_context_alloc(); + + /* Start the applicaton's main loop. It won't return until the application was requested to exit. */ + example_thermo_run(context); + + /* Release all unneeded resources */ + example_thermo_context_free(context); + + return 0; +} diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3d527f306c2d325fc72380856f14860f51b3742c GIT binary patch literal 7293 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 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/ba-BA.kl b/assets/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHkcVK4j(2-*&PMujecVX$$ zm1{Rv)^6RqGmA3vIK<6U^hikQGaw^p$cQl$rpzdqv!GYm`cD?ePm*T4GZVB*m| zTln^pGi$gvZr&YZh&>B_Ym yx9;4VM-@dp{CKxUod!)>wCT{LN1p*hMvR#-WyYKZOIEDeuw}=dEbdXpBj69fFAeVi literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-CH.kl b/assets/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 0000000000000000000000000000000000000000..1704bc9dbaae9fcc90213cb33b0132bca1b71866 GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5i<8!$oa;st31l>ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-DE.kl b/assets/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 0000000000000000000000000000000000000000..67b05c042f1649ab6aa17c4d369b096c88ec2785 GIT binary patch literal 256 zcmaLLHx9y30KiboqW2Pd4WuBN!ay1SVRHX7Ftgz88Gbu+;q<%y>dMxg4`L4uK0?Afwu&6#5(8&;hd+qL7+fvJ5TwhjIGHgM$E s3wH`~pT4NjqeYV%GsZ+Ts4^j@ONSu?Qu;h-6Y}9rz=$sw?-ss^@Y|jPyWjO!M`q5wk-2p3gN<8r z&%QYE=EIg5a~5PQS+OQ(!`(YC&%FKkuw>baRci*;Z5Z0LW!sLCv58$%d-feTbmZ8H tQ!f-;S$XnFi82+c)Cj24Af!o)HXR~j61t@H=rdr*h%pnUTr9kL6TT^X3*7(! literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/en-US.kl b/assets/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 0000000000000000000000000000000000000000..8089d8257881765fe67691a206b6bf6e9cb2c97d GIT binary patch literal 256 zcmaLL#}dH+007aQ9YGK+QKLn)QG!wb|I3_nvA4HS?#PKldHvOyg-=&zuHE=#=iaS_ zZ!UcJvS-1P6&Y)CHf-7P@WI=Hk6)Ko46ItSZfL`%EhF1@?Ao($Y~sM7BgamhnmTjt sl{+_bFTSV{P^CtlkOoa!M6~J9rAMEbgaJcFjF~VcWyYMVcW=Ig9}}MoYybcN literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/es-ES.kl b/assets/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 0000000000000000000000000000000000000000..15e9d7997c3f5178982fe212b5341f09469486bc GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5lA#$@e+Y@}VF{DLgOx;B0)}2=l z9xW`m^W?$a6A39BGPdm4bKuCsyDv_@yqZSIvK6b=tlO|@%eEc6_Ut=w=*Y1Xr_P+a zaOujmdHhEaxleOSl&MgqMx6#tTD0lVrAMCuLq?35FlEM^1xr?}`HLiOkp;W~w>u5X literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-BE.kl b/assets/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 0000000000000000000000000000000000000000..ea9e553e894a470639ee48648a386898ed5cfa67 GIT binary patch literal 256 zcmaLLNm9Z96adk#F6Mcj2}uy4P%#!3Wcd%1`=7xz8+-Q}Uc2(a<#qk5PfnzyQSfU* zM$Q+7r6X=Ue0nzVU}|RW(Kk=N`{Y4IN#)2x%&gn6Y0I`9yY}omaOmB!6Q|akyKw2s zwR^Yj+?YodMSOGfCuY=X(4ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-FR.kl b/assets/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 0000000000000000000000000000000000000000..f9193297e58722fd4f1547cb9ef62474beb2487b GIT binary patch literal 256 zcmaLLH*Nv}6adlDh@5i{8(2U#EVi(~}3$%7XqF9)7tV%3^;8#Zm(wqw_xeeVt(Ikw`|nR6E| z-MV(;$}Ebw#*KnQOsG<$PJ<>b+H~mBqtBfoBgRyiGGoqyC0o{PSdm5^W$Xj~0L-fm A_W%F@ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/hr-HR.kl b/assets/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk)x=fVv{{L5{w#jy9;k{43_;_Fc>YIyakMR>% z$I*KC;Silq2439!rR7Y=g)29D?mT$;j68aqcv{8C#IX~nre@BZyKw2s+`_dROSkUa zd$6+hXyeH=%E+RN!9GSLjF~VcC1b{%1xr@s6s*}$vSr7f0~Iw#8crlpM-gqnA2aL@ AGynhq literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/it-IT.kl b/assets/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 0000000000000000000000000000000000000000..059e428808a07b26d0da37d7f37d5d8d4614365b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>eeY5fH(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvm0r4@+k!5$Wx$5i83J(6{^&z)1XO96ECB#K@^L=Pq0t xyK-$Ft`y%!)Nxcub+H~j=&?BVJfFUEsOqeobPMlv%@{{ldJ?{*0 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/pt-BR.kl b/assets/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 0000000000000000000000000000000000000000..d36421cfc45747687dba3cabe05ff84a4550050a GIT binary patch literal 256 zcmaKnM-ssR004J(1W9z!q7!8hWkx%CjQ_uZtBbwu-WGn@v1j|6{@Q_wH)qaWn0aIE z#+66!wV)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk17S!45fq0~DYekR@*l?gpTRX7d-ob{d*zLn+x4&B`SxrR z=Qx?hB0l}{*Orbe7e0Kp_0GKq?|ty-qfcJ^p^Fo{=&Yh=Vrt)knYlwpj-5DlX5rk0 zrAt??-B?-MxN~b3W#qAstHmjD0& literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/sv-SE.kl b/assets/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 0000000000000000000000000000000000000000..5c55bb9ef1df3785fb143eb463a5375be10ce96f GIT binary patch literal 256 zcmaLLH*x|26u?l8hMaSTC2U}9uLl~S{D;~5pMjbNbx-iw55HVL*H`^JB+m2m(_8mBMwCT{LN1p*hMvR#-WyYKZOIEDeuw}=dG^!}#7VrkLXASNE literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/tr-TR.kl b/assets/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 0000000000000000000000000000000000000000..6377b770365ed544824108765ce2587f315bc1da GIT binary patch literal 256 zcmaLLM{dFZ6adlDh~A6oU~F)Sk{HDcB$od$x&IlmW|P^yf%iW7;^Te&t8Y@DWRb_s zyH`sq8**D7yeOY}m46&w(Q+&Ri(sH|qEa_ygc8 B4s!qi literal 0 HcmV?d00001 diff --git a/assets/resources/infrared/assets/projector.ir b/assets/resources/infrared/assets/projector.ir new file mode 100644 index 000000000..e9861de21 --- /dev/null +++ b/assets/resources/infrared/assets/projector.ir @@ -0,0 +1,829 @@ +Filetype: IR library file +Version: 1 +# +# Model: Smart +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8A 00 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +# Model: Hitatchi +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 +# +name: Vol_up +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 49 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 44 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 83 7C 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 82 7D 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 13 00 00 +command: 87 78 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 +# +name: Mute +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 29 D6 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 08 F7 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 04 FB 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 93 6C 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 1D 00 00 00 +# +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 +# AV-Mute +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 4C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 529 7218 126 6585 219 703 180 5362 427 18618 177 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620 +# ON +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 46 B9 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0E F1 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0D F2 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 08 16 00 00 +command: C8 37 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 28 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 29 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1D E2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 99 66 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 98 67 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 52 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 51 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 56 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 5A 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 82 7D 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 83 7C 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 91 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 31 00 00 00 +command: D0 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 89 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 30 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 31 00 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 32 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9031 4479 596 560 572 558 564 566 566 1693 571 1688 566 563 569 561 571 559 563 1696 568 561 571 559 563 1697 567 562 570 1689 565 1694 570 1688 566 1693 571 1661 593 1693 571 558 564 566 566 564 568 1691 563 541 591 564 568 562 570 560 562 1697 567 1692 562 1696 568 562 570 1689 565 564 568 561 571 559 563 567 565 564 568 562 570 560 562 567 565 1694 570 1689 565 1694 570 1688 566 1693 571 1688 566 1693 571 1662 592 13987 9031 2220 590 96231 9034 2217 593 96234 9030 2222 588 96247 9037 2215 595 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 14 00 00 00 +# OFF +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 1D 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 15 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: Vol_up +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: Vol_dn +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 18 E9 00 00 +command: 49 B6 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 14 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 40 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1F E0 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 81 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8F 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: A8 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 88 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 9C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 50 AF 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635 +# +name: Power +type: parsed +protocol: NECext +address: FF FF 00 00 +command: E8 17 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: FF FF 00 00 +command: BD 42 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: FF FF 00 00 +command: F2 0D 00 00 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +# +name: Vol_dn +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 71 01 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 81 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 \ No newline at end of file diff --git a/assets/resources/subghz/assets/alutech_at_4n b/assets/resources/subghz/assets/alutech_at_4n new file mode 100644 index 000000000..5d7beacec --- /dev/null +++ b/assets/resources/subghz/assets/alutech_at_4n @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz Keystore RAW File +Version: 0 +Encryption: 1 +IV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31 +Encrypt_data: RAW +E811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60 \ No newline at end of file diff --git a/assets/unit_tests/subghz/alutech_at_4n_raw.sub b/assets/unit_tests/subghz/alutech_at_4n_raw.sub new file mode 100644 index 000000000..ae5db9715 --- /dev/null +++ b/assets/unit_tests/subghz/alutech_at_4n_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/assets/unit_tests/subghz/dooya.sub b/assets/unit_tests/subghz/dooya.sub new file mode 100644 index 000000000..0767a1a73 --- /dev/null +++ b/assets/unit_tests/subghz/dooya.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Dooya +Bit: 40 +Key: 00 00 00 E1 DC 03 05 11 diff --git a/assets/unit_tests/subghz/dooya_raw.sub b/assets/unit_tests/subghz/dooya_raw.sub new file mode 100644 index 000000000..6c3ca1627 --- /dev/null +++ b/assets/unit_tests/subghz/dooya_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub new file mode 100644 index 000000000..49b190002 --- /dev/null +++ b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/assets/unit_tests/subghz/linear_delta3.sub b/assets/unit_tests/subghz/linear_delta3.sub new file mode 100644 index 000000000..f00507428 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: LinearDelta3 +Bit: 8 +Key: 00 00 00 00 00 00 00 D0 diff --git a/assets/unit_tests/subghz/linear_delta3_raw.sub b/assets/unit_tests/subghz/linear_delta3_raw.sub new file mode 100644 index 000000000..1973622a5 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985 diff --git a/assets/unit_tests/subghz/nice_one_raw.sub b/assets/unit_tests/subghz/nice_one_raw.sub new file mode 100644 index 000000000..169b3f088 --- /dev/null +++ b/assets/unit_tests/subghz/nice_one_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md new file mode 100644 index 000000000..325f640d7 --- /dev/null +++ b/documentation/UniversalRemotes.md @@ -0,0 +1,76 @@ +# Universal Remotes + +## Televisions + +Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV. + +Each signal is recorded using the following algorithm: + +1. Get the remote and point it to Flipper's IR receiver. +2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise. +3. Press a remote button and save it under a corresponding name. +4. Repeat steps 2-3 until all required signals are saved. + +The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [TV universal remote file](/assets/resources/infrared/assets/tv.ir). + +## Audio players + +Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player. + +The signal names are self-explanatory. +On many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`. +Make sure that every signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [audio player universal remote file](/assets/resources/infrared/assets/audio.ir). + +## Projectors + +Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. +To save time, please make sure every recording has been named accordingly. +In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. + + +## Air conditioners + +Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. +The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings. +When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole. + +In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`. +Each signal (except `Off`) is recorded using the following algorithm: + +1. Get the remote and press the **Power Button** so that the display shows that A/C is ON. +2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable). +3. Press the **POWER** button to switch the A/C off. +4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise. +5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again. +6. Save the resulting signal under the specified name. +7. Repeat steps 2-6 for each signal from the table below. + +| Signal | Mode | Temperature | Note | +| :-----: | :--------: | :---------: | ----------------------------------- | +| Dh | Dehumidify | N/A | | +| Cool_hi | Cooling | See note | Lowest temperature in cooling mode | +| Cool_lo | Cooling | 23°C | | +| Heat_hi | Heating | See note | Highest temperature in heating mode | +| Heat_lo | Heating | 23°C | | + +Finally, record the `Off` signal: + +1. Make sure the display shows that the A/C is ON. +2. Start learning a new signal on Flipper and point the remote towards the IR receiver. +3. Press the **POWER** button so that the remote shows the OFF state. +4. Save the resulting signal under the name `Off`. + +The resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality. +Test the file against the actual device. Make sure that every signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). + +## Final steps + +The order of signals is not important, but they should be preceded by the following comment: `# Model: ` in order to keep the library organized. + +When done, open a pull request containing the changed file. diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3d88c7cc1..3a687f32e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.8,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1348,8 +1348,7 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -2651,12 +2650,14 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* +Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" @@ -2679,7 +2680,7 @@ Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8 Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" -Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" @@ -2688,6 +2689,14 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" +Function,+,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_alutech_at_4n_free,void,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_alutech_at_4n_reset,void,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" @@ -2757,6 +2766,14 @@ Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_doitrand_reset,void,void* Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_dooya_free,void,void* +Function,+,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_dooya_reset,void,void* +Function,+,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" @@ -2837,7 +2854,23 @@ Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_kia_reset,void,void* Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_linear_delta3_free,void,void* +Function,+,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_linear_delta3_reset,void,void* +Function,+,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_linear_free,void,void* @@ -3032,6 +3065,11 @@ Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFor Function,-,subghz_protocol_encoder_doitrand_free,void,void* Function,-,subghz_protocol_encoder_doitrand_stop,void,void* Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_dooya_free,void,void* +Function,+,subghz_protocol_encoder_dooya_stop,void,void* +Function,+,subghz_protocol_encoder_dooya_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_faac_slh_free,void,void* @@ -3074,6 +3112,11 @@ Function,-,subghz_protocol_encoder_keeloq_free,void,void* Function,-,subghz_protocol_encoder_keeloq_stop,void,void* Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_linear_delta3_free,void,void* +Function,+,subghz_protocol_encoder_linear_delta3_stop,void,void* +Function,+,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_linear_free,void,void* Function,-,subghz_protocol_encoder_linear_stop,void,void* @@ -3148,6 +3191,16 @@ Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperForm Function,-,subghz_protocol_encoder_smc5326_free,void,void* Function,-,subghz_protocol_encoder_smc5326_stop,void,void* Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_somfy_keytis_free,void,void* +Function,+,subghz_protocol_encoder_somfy_keytis_stop,void,void* +Function,+,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_somfy_telis_free,void,void* +Function,+,subghz_protocol_encoder_somfy_telis_stop,void,void* +Function,+,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_star_line_free,void,void* @@ -3169,6 +3222,8 @@ Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const Su Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,+,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,+,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" @@ -4879,6 +4934,9 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,+,subghz_protocol_alutech_at_4n,const SubGhzProtocol, +Variable,+,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, @@ -4903,6 +4961,9 @@ Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_dooya,const SubGhzProtocol, +Variable,+,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, @@ -4933,8 +4994,14 @@ Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_kia,const SubGhzProtocol, Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, +Variable,+,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_linear,const SubGhzProtocol, Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_linear_delta3,const SubGhzProtocol, +Variable,+,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_magellan,const SubGhzProtocol, Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c new file mode 100644 index 000000000..93b837e85 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -0,0 +1,877 @@ +#include "sd_spi_io.h" +#include "sector_cache.h" +#include +#include +#include + +// #define SD_SPI_DEBUG 1 +#define TAG "SdSpi" + +#ifdef SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH 6 +#define SD_DUMMY_BYTE 0xFF +#define SD_ANSWER_RETRY_COUNT 8 +#define SD_IDLE_RETRY_COUNT 100 +#define SD_BLOCK_SIZE 512 + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +static inline void sd_spi_select_card() { + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); + furi_delay_us(10); // Entry guard time for some SD cards +} + +static inline void sd_spi_deselect_card() { + furi_delay_us(10); // Exit guard time for some SD cards + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); +} + +static void sd_spi_bus_to_ground() { + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + + sd_spi_select_card(); + furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); +} + +static void sd_spi_bus_rise_up() { + sd_spi_deselect_card(); + + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); +} + +static inline uint8_t sd_spi_read_byte(void) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_byte(uint8_t data) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); +} + +static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static uint8_t sd_spi_wait_for_data_and_read(void) { + uint8_t retry_count = SD_ANSWER_RETRY_COUNT; + uint8_t responce; + + // Wait until we get a valid data + do { + responce = sd_spi_read_byte(); + retry_count--; + + } while((responce == SD_DUMMY_BYTE) && retry_count); + + return responce; +} + +static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); + uint8_t byte; + + do { + byte = sd_spi_read_byte(); + if(furi_hal_cortex_timer_is_expired(timer)) { + return SdSpiStatusTimeout; + } + } while((byte != data)); + + return SdSpiStatusOK; +} + +static inline void sd_spi_deselect_card_and_purge() { + sd_spi_deselect_card(); + sd_spi_read_byte(); +} + +static inline void sd_spi_purge_crc() { + sd_spi_read_byte(); + sd_spi_read_byte(); +} + +static SdSpiCmdAnswer + sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { + uint8_t frame[SD_CMD_LENGTH]; + SdSpiCmdAnswer cmd_answer = { + .r1 = SD_DUMMY_BYTE, + .r2 = SD_DUMMY_BYTE, + .r3 = SD_DUMMY_BYTE, + .r4 = SD_DUMMY_BYTE, + .r5 = SD_DUMMY_BYTE, + }; + + // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes + // R1b identical to R1 + Busy information + // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes + + frame[0] = ((uint8_t)cmd | 0x40); + frame[1] = (uint8_t)(arg >> 24); + frame[2] = (uint8_t)(arg >> 16); + frame[3] = (uint8_t)(arg >> 8); + frame[4] = (uint8_t)(arg); + frame[5] = (crc | 0x01); + + sd_spi_select_card(); + sd_spi_write_bytes(frame, sizeof(frame)); + + switch(answer_type) { + case SdSpiCmdAnswerTypeR1: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + break; + case SdSpiCmdAnswerTypeR1B: + // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + + // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B + // reassert card + sd_spi_deselect_card(); + furi_delay_us(1000); + sd_spi_deselect_card(); + + // and wait for it to be ready + while(sd_spi_read_byte() != 0xFF) { + }; + + break; + case SdSpiCmdAnswerTypeR2: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + break; + case SdSpiCmdAnswerTypeR3: + case SdSpiCmdAnswerTypeR7: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + cmd_answer.r3 = sd_spi_read_byte(); + cmd_answer.r4 = sd_spi_read_byte(); + cmd_answer.r5 = sd_spi_read_byte(); + break; + default: + break; + } + return cmd_answer; +} + +static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { + SdSpiDataResponce responce = sd_spi_read_byte(); + // read busy response byte + sd_spi_read_byte(); + + switch(responce & 0x1F) { + case SdSpiDataResponceOK: + // TODO: check timings + sd_spi_deselect_card(); + sd_spi_select_card(); + + // wait for 0xFF + if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static SdSpiStatus sd_spi_init_spi_mode_v1(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v1"); + + do { + retry_count++; + + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode_v2(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v2"); + + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + sd_spi_debug("ACMD41 is illegal command"); + retry_count = 0; + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { + sd_spi_debug("CMD55 failed"); + return SdSpiStatusError; + } + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode(void) { + SdSpiCmdAnswer response; + uint8_t retry_count; + + // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and + // wait for In Idle State Response (R1 Format) equal to 0x01 + retry_count = 0; + do { + retry_count++; + response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("CMD0 failed"); + return SdSpiStatusError; + } + } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); + + // CMD8 (SEND_IF_COND) to check the power supply status + // and wait until response (R7 Format) equal to 0xAA and + response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); + sd_spi_deselect_card_and_purge(); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) { + sd_spi_debug("Init mode v1 failed"); + return SdSpiStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) { + sd_spi_debug("Init mode v2 failed"); + return SdSpiStatusError; + } + + // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response + response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_debug("CMD58 failed"); + return SdSpiStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return SdSpiStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD9 (SEND_CSD): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CSD data + for(counter = 0; counter < 16; counter++) { + csd_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + /************************************************************************* + CSD header decoding + *************************************************************************/ + + csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; + csd->Reserved1 = csd_data[0] & 0x3F; + csd->TAAC = csd_data[1]; + csd->NSAC = csd_data[2]; + csd->MaxBusClkFrec = csd_data[3]; + csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); + csd->RdBlockLen = csd_data[5] & 0x0F; + csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; + csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; + csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; + csd->DSRImpl = (csd_data[6] & 0x10) >> 4; + + /************************************************************************* + CSD v1/v2 decoding + *************************************************************************/ + + if(sd_high_capacity == 0) { + csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); + csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | + ((csd_data[8] & 0xC0) >> 6); + csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; + csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); + csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; + csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; + csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | + ((csd_data[10] & 0x80) >> 7); + } else { + csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | + ((csd_data[7] & 0xC0) >> 6); + csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | + csd_data[9]; + csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); + } + + csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; + csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); + csd->WrProtectGrSize = (csd_data[11] & 0x7F); + csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; + csd->Reserved2 = (csd_data[12] & 0x60) >> 5; + csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; + csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); + csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; + csd->Reserved3 = (csd_data[13] & 0x1F); + csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; + csd->CopyFlag = (csd_data[14] & 0x40) >> 6; + csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; + csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; + csd->FileFormat = (csd_data[14] & 0x0C) >> 2; + csd->Reserved4 = (csd_data[14] & 0x03); + csd->crc = (csd_data[15] & 0xFE) >> 1; + csd->Reserved5 = (csd_data[15] & 0x01); + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD10 (SEND_CID): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CID data + for(counter = 0; counter < 16; counter++) { + cid_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + Cid->ManufacturerID = cid_data[0]; + memcpy(Cid->OEM_AppliID, cid_data + 1, 2); + memcpy(Cid->ProdName, cid_data + 3, 5); + Cid->ProdRev = cid_data[8]; + Cid->ProdSN = cid_data[9] << 24; + Cid->ProdSN |= cid_data[10] << 16; + Cid->ProdSN |= cid_data[11] << 8; + Cid->ProdSN |= cid_data[12]; + Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; + Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; + Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; + Cid->Reserved2 = 1; + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static SdSpiStatus + sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + SdSpiStatusOK) { + // Read the data block + sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } else { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_cmd_write_blocks( + uint32_t* data, + uint32_t address, + uint32_t blocks, + uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors) + response = sd_spi_send_cmd( + SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN + // TODO: check bytes count + sd_spi_write_byte(SD_DUMMY_BYTE); + sd_spi_write_byte(SD_DUMMY_BYTE); + + // Send the data start token + sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); + sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // Read data response + SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); + sd_spi_deselect_card_and_purge(); + + if(data_responce != SdSpiDataResponceOK) { + return SdSpiStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return SdSpiStatusOK; +} + +uint8_t sd_max_mount_retry_count() { + return 10; +} + +SdSpiStatus sd_init(bool power_reset) { + // Slow speed init + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; + + // We reset card in spi_lock context, so it is safe to disturb spi bus + if(power_reset) { + sd_spi_debug("Power reset"); + + // disable power and set low on all bus pins + furi_hal_power_disable_external_3_3v(); + sd_spi_bus_to_ground(); + hal_sd_detect_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + hal_sd_detect_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + SdSpiStatus status = SdSpiStatusError; + + // Send 80 dummy clocks with CS high + sd_spi_deselect_card(); + for(uint8_t i = 0; i < 80; i++) { + sd_spi_write_byte(SD_DUMMY_BYTE); + } + + for(uint8_t i = 0; i < 128; i++) { + status = sd_spi_init_spi_mode(); + if(status == SdSpiStatusOK) { + // SD initialized and init to SPI mode properly + sd_spi_debug("SD init OK after %d retries", i); + break; + } + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + + // Init sector cache + sector_cache_init(); + + return status; +} + +SdSpiStatus sd_get_card_state(void) { + SdSpiCmdAnswer response; + + // Send CMD13 (SEND_STATUS) to get SD status + response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); + sd_spi_deselect_card_and_purge(); + + // Return status OK if response is valid + if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { + return SdSpiStatusOK; + } + + return SdSpiStatusError; +} + +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { + SdSpiStatus status; + + status = sd_spi_get_csd(&(card_info->Csd)); + + if(status != SdSpiStatusOK) { + return status; + } + + status = sd_spi_get_cid(&(card_info->Cid)); + + if(status != SdSpiStatusOK) { + return status; + } + + if(sd_high_capacity == 1) { + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 512; + card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)card_info->LogBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } else { + card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1); + card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2)); + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen); + card_info->CardCapacity *= card_info->CardBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } + + return status; +} + +SdSpiStatus + sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + SdSpiStatus status = SdSpiStatusError; + + bool single_sector_read = (blocks == 1); + + if(single_sector_read) { + if(sd_cache_get(address, data)) { + return SdSpiStatusOK; + } + + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + + if(status == SdSpiStatusOK) { + sd_cache_put(address, data); + } + } else { + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + } + + return status; +} + +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + sd_cache_invalidate_range(address, address + blocks); + SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); + return status; +} + +SdSpiStatus sd_get_cid(SD_CID* cid) { + SdSpiStatus status; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + memset(cid, 0, sizeof(SD_CID)); + status = sd_spi_get_cid(cid); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h new file mode 100644 index 000000000..8850eceb7 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -0,0 +1,157 @@ +#pragma once +#include +#include + +#define __IO volatile + +#define SD_TIMEOUT_MS (1000) + +typedef enum { + SdSpiStatusOK, + SdSpiStatusError, + SdSpiStatusTimeout, +} SdSpiStatus; + +/** + * @brief Card Specific Data: CSD Register + */ +typedef struct { + /* Header part */ + uint8_t CSDStruct : 2; /* CSD structure */ + uint8_t Reserved1 : 6; /* Reserved */ + uint8_t TAAC : 8; /* Data read access-time 1 */ + uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ + uint16_t CardComdClasses : 12; /* Card command classes */ + uint8_t RdBlockLen : 4; /* Max. read data block length */ + uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ + uint8_t WrBlockMisalign : 1; /* Write block misalignment */ + uint8_t RdBlockMisalign : 1; /* Read block misalignment */ + uint8_t DSRImpl : 1; /* DSR implemented */ + + /* v1 or v2 struct */ + union csd_version { + struct { + uint8_t Reserved1 : 2; /* Reserved */ + uint16_t DeviceSize : 12; /* Device Size */ + uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ + uint8_t DeviceSizeMul : 3; /* Device size multiplier */ + } v1; + struct { + uint8_t Reserved1 : 6; /* Reserved */ + uint32_t DeviceSize : 22; /* Device Size */ + uint8_t Reserved2 : 1; /* Reserved */ + } v2; + } version; + + uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ + uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ + uint8_t WrProtectGrSize : 7; /* Write protect group size */ + uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ + uint8_t Reserved2 : 2; /* Reserved */ + uint8_t WrSpeedFact : 3; /* Write speed factor */ + uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ + uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ + uint8_t Reserved3 : 5; /* Reserved */ + uint8_t FileFormatGrouop : 1; /* File format group */ + uint8_t CopyFlag : 1; /* Copy flag (OTP) */ + uint8_t PermWrProtect : 1; /* Permanent write protection */ + uint8_t TempWrProtect : 1; /* Temporary write protection */ + uint8_t FileFormat : 2; /* File Format */ + uint8_t Reserved4 : 2; /* Reserved */ + uint8_t crc : 7; /* Reserved */ + uint8_t Reserved5 : 1; /* always 1*/ + +} SD_CSD; + +/** + * @brief Card Identification Data: CID Register + */ +typedef struct { + uint8_t ManufacturerID; /* ManufacturerID */ + char OEM_AppliID[2]; /* OEM/Application ID */ + char ProdName[5]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + uint32_t ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint8_t ManufactYear; /* Manufacturing Year */ + uint8_t ManufactMonth; /* Manufacturing Month */ + uint8_t CID_CRC; /* CID CRC */ + uint8_t Reserved2; /* always 1 */ +} SD_CID; + +/** + * @brief SD Card information structure + */ +typedef struct { + SD_CSD Csd; + SD_CID Cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ + uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ +} SD_CardInfo; + +/** + * @brief SD card max mount retry count + * + * @return uint8_t + */ +uint8_t sd_max_mount_retry_count(); + +/** + * @brief Init sd card + * + * @param power_reset reset card power + * @return SdSpiStatus + */ +SdSpiStatus sd_init(bool power_reset); + +/** + * @brief Get card state + * + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_state(void); + +/** + * @brief Get card info + * + * @param card_info + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info); + +/** + * @brief Read blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Write blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Get card CSD register + * + * @param Cid + * @return SdSpiStatus + */ +SdSpiStatus sd_get_cid(SD_CID* cid); \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2f9d87080..42b854799 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,14 +1,33 @@ +#include #include #include #include +#include -#include -#include - +#include #include #include #include +#define TAG "FuriHalSpi" + +#define SPI_DMA DMA2 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL +#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL + +// For simplicity, I assume that only one SPI DMA transaction can occur at a time. +static FuriSemaphore* spi_dma_lock = NULL; +static FuriSemaphore* spi_dma_completed = NULL; + +void furi_hal_spi_dma_init() { + spi_dma_lock = furi_semaphore_alloc(1, 1); + spi_dma_completed = furi_semaphore_alloc(1, 1); +} + void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); @@ -84,7 +103,7 @@ bool furi_hal_spi_bus_rx( bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout) { furi_assert(handle); @@ -109,7 +128,7 @@ bool furi_hal_spi_bus_tx( bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) { @@ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx( return ret; } + +static void spi_dma_isr() { +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC3(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC4(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif +} + +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + // If scheduler is not running, use blocking mode + if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); + } + + // Lock DMA + furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); + + const uint32_t dma_dummy_u32 = 0xFFFFFFFF; + + bool ret = true; + SPI_TypeDef* spi = handle->bus->spi; + uint32_t dma_rx_req; + uint32_t dma_tx_req; + + if(spi == SPI1) { + dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; + } else if(spi == SPI2) { + dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; + } else { + furi_crash(NULL); + } + + if(rx_buffer == NULL) { + // Only TX mode, do not use RX channel + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + LL_DMA_ClearFlag_TC4(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + } else { + // TRX or RX mode, use both channels + uint32_t tx_mem_increase_mode; + + if(tx_buffer == NULL) { + // RX mode, use dummy data instead of TX buffer + tx_buffer = (uint8_t*)&dma_dummy_u32; + tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; + } else { + tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; + } + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + LL_DMA_ClearFlag_TC3(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); + + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); + + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); + + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); + } + + furi_hal_spi_bus_end_txrx(handle, timeout_ms); + + furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); + + return ret; +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index ab00ef0d7..9c54befb4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -71,38 +71,23 @@ bool furi_hal_spi_bus_rx( size_t size, uint32_t timeout); -/** SPI Transmit - * - * @param handle pointer to FuriHalSpiBusHandle instance - * @param buffer transmit buffer - * @param size transaction size (buffer size) - * @param timeout operation timeout in ms - * - * @return true on success - */ -bool furi_hal_spi_bus_tx( - FuriHalSpiBusHandle* handle, - uint8_t* buffer, - size_t size, - uint32_t timeout); - -/** SPI Transmit and Receive +/** SPI Transmit and Receive with DMA * * @param handle pointer to FuriHalSpiBusHandle instance * @param tx_buffer pointer to tx buffer * @param rx_buffer pointer to rx buffer * @param size transaction size (buffer size) - * @param timeout operation timeout in ms + * @param timeout_ms operation timeout in ms * * @return true on success */ -bool furi_hal_spi_bus_trx( +bool furi_hal_spi_bus_trx_dma( FuriHalSpiBusHandle* handle, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, - uint32_t timeout); + uint32_t timeout_ms); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index f3349b5fc..e325be21c 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -2,6 +2,8 @@ #include "math.h" #include +#include "furi.h" + #define TAG "SubGhzBlockEncoder" void subghz_protocol_blocks_set_bit_array( @@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); } -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit) { - size_t index_bit = 0; + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; size_t size_upload = 0; uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1; i < count_bit_data_array; i++) { + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { duration += duration_bit; } else { - furi_assert(max_size_upload > size_upload); + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); last_bit = !last_bit; @@ -42,4 +55,4 @@ size_t subghz_protocol_blocks_get_upload( upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); return size_upload; -} +} \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 1ff077726..aeaa2add0 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -19,6 +19,11 @@ typedef struct { } SubGhzProtocolBlockEncoder; +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + /** * Set data bit when encoding HEX array. * @param bit_value The value of the bit to be set @@ -47,13 +52,15 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde * @param upload Pointer to a LevelDuration * @param max_size_upload upload size, check not to overflow * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array */ -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit); + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); #ifdef __cplusplus } diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 0a4b7b606..b39b259d4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -6,6 +6,7 @@ struct SubGhzEnvironment { const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; }; SubGhzEnvironment* subghz_environment_alloc() { @@ -57,6 +58,21 @@ const char* return instance->came_atomo_rainbow_table_file_name; } +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + void subghz_environment_set_nice_flor_s_rainbow_table_file_name( SubGhzEnvironment* instance, const char* filename) { diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index e994f7c97..7bd38ba2f 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -52,6 +52,23 @@ void subghz_environment_set_came_atomo_rainbow_table_file_name( */ const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + /** * Set filename to work with Nice Flor-S. * @param instance Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..3fef76af2 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeStatic, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} \ No newline at end of file diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 32f4e9520..9c8e5ee4a 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -196,12 +196,13 @@ static bool break; } - instance->encoder.size_upload = subghz_protocol_blocks_get_upload( + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( upload_hex_data, upload_hex_count_bit, instance->encoder.upload, instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short); + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); return true; } diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/lib/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/lib/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index aaf573a57..a0970de4d 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -520,11 +520,14 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur subghz_protocol_keeloq_const.te_delta)) { // Found end TX instance->decoder.parser_step = KeeloqDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -541,6 +544,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( @@ -551,6 +556,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else { diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..2c8de0d2d --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,336 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + uint64_t data, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->data, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->data, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..c9f1cf380 --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 5ca95901d..6447676cc 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -14,6 +14,9 @@ #define TAG "SubGhzProtocolNiceFlorS" +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { .te_short = 500, .te_long = 1000, @@ -28,6 +31,7 @@ struct SubGhzProtocolDecoderNiceFlorS { SubGhzBlockGeneric generic; const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; }; struct SubGhzProtocolEncoderNiceFlorS { @@ -243,6 +247,64 @@ LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { return ret; } +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -427,10 +489,14 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -464,6 +530,11 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ } else { instance->decoder.parser_step = NiceFlorSDecoderStepReset; } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } break; } } @@ -477,6 +548,7 @@ static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name) { /* + * Protocol Nice Flor-S * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; * P1 (4-bit) - batch repetition number, calculated by the formula: @@ -497,6 +569,24 @@ static void subghz_protocol_nice_flor_s_remote_controller( * data => 0x1c5783607f7b3 key serial cnt * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * */ if(!file_name) { instance->cnt = 0; @@ -523,7 +613,15 @@ bool subghz_protocol_decoder_nice_flor_s_serialize( SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; } bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { @@ -534,11 +632,25 @@ bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperForma if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if(instance->generic.data_count_bit != - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + ret = true; } while(false); return ret; @@ -550,20 +662,33 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } } diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 5f733d6bd..96717e56f 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,21 +1,50 @@ #include "protocol_items.h" const SubGhzProtocol* subghz_protocol_registry_items[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, - &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_pocsag, - &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &subghz_protocol_chamb_code, + &subghz_protocol_power_smart, + &subghz_protocol_marantec, + &subghz_protocol_bett, + &subghz_protocol_doitrand, + &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell_wdb, + &subghz_protocol_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_pocsag, + &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, }; const SubGhzProtocolRegistry subghz_protocol_registry = { .items = subghz_protocol_registry_items, - .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 55a1ea860..362b0459a 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -21,6 +21,7 @@ #include "gate_tx.h" #include "raw.h" #include "linear.h" +#include "linear_delta3.h" #include "secplus_v2.h" #include "secplus_v1.h" #include "megacode.h" @@ -39,6 +40,9 @@ #include "pocsag.h" #include "smc5326.h" #include "holtek_ht12x.h" +#include "dooya.h" +#include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" #ifdef __cplusplus extern "C" { diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 9c3afeb46..3ef95db36 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -606,7 +606,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03lX\r\n" + "Cnt:0x%03lX " "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, @@ -625,7 +625,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03lX\r\n" + "Cnt:0x%03lX " "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 63a3e26d2..ab9202cc3 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -55,25 +55,40 @@ const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = { .get_string = subghz_protocol_decoder_somfy_keytis_get_string, }; -const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - const SubGhzProtocol subghz_protocol_somfy_keytis = { .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_somfy_keytis_decoder, .encoder = &subghz_protocol_somfy_keytis_encoder, }; +const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { + .alloc = subghz_protocol_encoder_somfy_keytis_alloc, + .free = subghz_protocol_encoder_somfy_keytis_free, + + .deserialize = subghz_protocol_encoder_somfy_keytis_deserialize, + .stop = subghz_protocol_encoder_somfy_keytis_stop, + .yield = subghz_protocol_encoder_somfy_keytis_yield, +}; + +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis)); + + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis)); @@ -83,6 +98,13 @@ void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) return instance; } +void subghz_protocol_encoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + free(instance->encoder.upload); + free(instance); +} + void subghz_protocol_decoder_somfy_keytis_free(void* context) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; @@ -100,6 +122,330 @@ void subghz_protocol_decoder_somfy_keytis_reset(void* context) { NULL); } +static bool + subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 48) & 0xF; + instance->generic.cnt = (data >> 24) & 0xFFFF; + instance->generic.serial = data & 0xFFFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[10]; + frame[0] = (0xA << 4) | instance->generic.btn; + frame[1] = 0xF << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + frame[7] = 0xC4; + frame[8] = 0x00; + frame[9] = 0x19; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + data = 0; + for(uint8_t i = 7; i < 10; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data_2 = data; + return true; +} + +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 80; + bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_keytis_get_upload( + SubGhzProtocolEncoderSomfyKeytis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + + for(uint8_t i = 0; i < 2; ++i) { + //Hardware sync + for(uint8_t i = 0; i < 6; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + } + + //Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + + size_t size_upload = index; + + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + return true; +} + +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_keytis_stop(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + /** * Сhecksum calculation. * @param data Вata for checksum calculation diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index 3b5950611..b08da3641 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -11,6 +11,58 @@ extern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder; extern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder; extern const SubGhzProtocol subghz_protocol_somfy_keytis; +/** + * Allocate SubGhzProtocolEncoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyKeytis* pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context); + /** * Allocate SubGhzProtocolDecoderSomfyKeytis. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 945a88405..96997c581 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -55,24 +55,301 @@ const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_somfy_telis_alloc, + .free = subghz_protocol_encoder_somfy_telis_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_somfy_telis_deserialize, + .stop = subghz_protocol_encoder_somfy_telis_stop, + .yield = subghz_protocol_encoder_somfy_telis_yield, }; const SubGhzProtocol subghz_protocol_somfy_telis = { .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_somfy_telis_decoder, .encoder = &subghz_protocol_somfy_telis_encoder, }; +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); + + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool + subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 44) & 0xF; // ctrl + instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code + instance->generic.serial = data & 0xFFFFFF; // address + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[7]; + frame[0] = data >> 48; + frame[1] = instance->generic.btn << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + return true; +} + +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 56; + bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_telis_get_upload( + SubGhzProtocolEncoderSomfyTelis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 2; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + + //Retransmission + for(uint8_t i = 0; i < 2; i++) { + //Hardware sync + for(uint8_t i = 0; i < 7; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + } + + size_t size_upload = index; + + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + return true; +} + +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_telis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_telis_stop(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + void* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis)); @@ -299,9 +576,9 @@ static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGener */ uint64_t data = instance->data ^ (instance->data >> 8); - instance->btn = (data >> 44) & 0xF; - instance->cnt = (data >> 24) & 0xFFFF; - instance->serial = data & 0xFFFFFF; + instance->btn = (data >> 44) & 0xF; // ctrl + instance->cnt = (data >> 24) & 0xFFFF; // rolling code + instance->serial = data & 0xFFFFFF; // address } /** @@ -309,7 +586,7 @@ static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGener * @param btn Button number, 4 bit */ static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { - const char* name_btn[0x10] = { + const char* name_btn[16] = { "Unknown", "My", "Up", @@ -368,7 +645,6 @@ void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* o SubGhzProtocolDecoderSomfyTelis* instance = context; subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); - furi_string_cat_printf( output, "%s %db\r\n" diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index a6a9fa5b2..b5e989866 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -11,6 +11,58 @@ extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; extern const SubGhzProtocol subghz_protocol_somfy_telis; +/** + * Allocate SubGhzProtocolEncoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyTelis* pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context); + /** * Allocate SubGhzProtocolDecoderSomfyTelis. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 733fc35b5..35ba54a8a 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -39,6 +39,7 @@ static const uint32_t subghz_frequency_list[] = { 330000000, 345000000, 348000000, + 350000000, /* 387 - 464 */ 387000000, @@ -75,10 +76,10 @@ static const uint32_t subghz_frequency_list[] = { }; static const uint32_t subghz_hopper_frequency_list[] = { - 310000000, 315000000, - 318000000, + 330000000, 390000000, + 433420000, 433920000, 868350000, 0, diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc..205cc2752 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,14 +104,16 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -134,7 +136,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 6c34dc728..1b8ef6a14 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -90,6 +90,7 @@ typedef enum { SubGhzProtocolFlag_Save = (1 << 7), SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; typedef struct { From 17db80794cc6c40c3d8d052049fafe8f75ba6418 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 18:46:40 +0000 Subject: [PATCH 58/75] Better asset pack git ignore --- .gitignore | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index dd487d0ce..bf7addba9 100644 --- a/.gitignore +++ b/.gitignore @@ -68,8 +68,13 @@ PVS-Studio.log # Automate files, etc automate.py deployments/ -assets/dolphin/custom/ -assets/resources/dolphin_custom/ fbt_options.py commitnotes.md lib/STM32CubeWB + +# Asset packs +assets/dolphin/custom/* +!assets/dolphin/custom/WatchDogs/ +!assets/dolphin/custom/ReadMe.md +assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/WatchDogs/ From dfb6e9daa6d52fa672864dae7733ac337a3081a7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:04:53 +0000 Subject: [PATCH 59/75] Fix spi header --- .../targets/furi_hal_include/furi_hal_spi.h | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index 9c54befb4..af15a8838 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ void furi_hal_spi_config_init(); +/** Initialize SPI DMA HAL */ +void furi_hal_spi_dma_init(); + /** Initialize SPI Bus * * @param handle pointer to FuriHalSpiBus instance @@ -71,6 +74,38 @@ bool furi_hal_spi_bus_rx( size_t size, uint32_t timeout); +/** SPI Transmit + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param buffer transmit buffer + * @param size transaction size (buffer size) + * @param timeout operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* buffer, + size_t size, + uint32_t timeout); + +/** SPI Transmit and Receive + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param tx_buffer pointer to tx buffer + * @param rx_buffer pointer to rx buffer + * @param size transaction size (buffer size) + * @param timeout operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_trx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout); + /** SPI Transmit and Receive with DMA * * @param handle pointer to FuriHalSpiBusHandle instance @@ -90,4 +125,4 @@ bool furi_hal_spi_bus_trx_dma( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From 7474ac193e863063137da392d533cbd6218b1d65 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:15:35 +0000 Subject: [PATCH 60/75] Kinda fix subghz gpio pin shenanigans --- lib/subghz/subghz_tx_rx_worker.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 205cc2752..42124bebc 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + while(furi_hal_gpio_read(&gpio_cc1101_g0)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,16 +104,14 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -136,7 +134,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); From 55d18eb8a4e2c03857b6790c6b30d30c4a7e9337 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:37:19 +0000 Subject: [PATCH 61/75] Kinda fix subghz protocols linker issues --- lib/subghz/SConscript | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 8fbec94ad..6d091114b 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -11,7 +11,6 @@ env.Append( File("subghz_tx_rx_worker.h"), File("transmitter.h"), File("registry.h"), - File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), From 5afe51589637fa8b4d227e1a8c3627a93896d53f Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:44:34 +0000 Subject: [PATCH 62/75] Fix missing canvas custom font api --- applications/services/gui/canvas.c | 6 ++++++ applications/services/gui/canvas.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 6a2bd3537..ee472138e 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -140,6 +140,12 @@ void canvas_set_font(Canvas* canvas, Font font) { } } +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) { + furi_assert(canvas); + u8g2_SetFontMode(&canvas->fb, 1); + u8g2_SetFont(&canvas->fb, font); +} + void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 9c03ef59a..9fb554be2 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -147,6 +147,13 @@ void canvas_invert_color(Canvas* canvas); */ void canvas_set_font(Canvas* canvas, Font font); +/** Set custom drawing font + * + * @param canvas Canvas instance + * @param font Pointer to u8g2 const uint8_t* font array + */ +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); + /** Draw string at position of baseline defined by x, y. * * @param canvas Canvas instance From 8b1179794916cdf54d3e6a6d8bd1685c014a9ec7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:53:25 +0000 Subject: [PATCH 63/75] Fix api symbols --- firmware/targets/f7/api_symbols.csv | 640 +--------------------------- 1 file changed, 5 insertions(+), 635 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3a687f32e..a38fe72fe 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,v,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -178,7 +178,6 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, -Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -535,8 +534,6 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* -Function,-,atomo_decrypt,void,uint8_t* -Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -645,6 +642,7 @@ Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,-,canvas_set_orientation,void,"Canvas*, CanvasOrientation" @@ -1348,10 +1346,13 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,+,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1745,8 +1746,6 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] -Function,-,keeloq_reset_kl_type,void, -Function,-,keeloq_reset_mfname,void, Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -2506,8 +2505,6 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." -Function,-,star_line_reset_kl_type,void, -Function,-,star_line_reset_mfname,void, Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" @@ -2689,275 +2686,11 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_alutech_at_4n_free,void,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ansonic_free,void,void* -Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" -Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bett_free,void,void* -Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_atomo_free,void,void* -Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_free,void,void* -Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_twee_free,void,void* -Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_chamb_code_free,void,void* -Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_clemsa_free,void,void* -Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_doitrand_free,void,void* -Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_dooya_free,void,void* -Function,+,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_dooya_reset,void,void* -Function,+,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_faac_slh_free,void,void* -Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_gate_tx_free,void,void* -Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_free,void,void* -Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_hormann_free,void,void* -Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ido_free,void,void* -Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_keeloq_free,void,void* -Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kia_free,void,void* -Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_linear_delta3_free,void,void* -Function,+,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,+,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_free,void,void* -Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellan_free,void,void* -Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_marantec_free,void,void* -Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_megacode_free,void,void* -Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_radio_free,void,void* -Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flo_free,void,void* -Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_power_smart_free,void,void* -Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_princeton_free,void,void* -Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_princeton_reset,void,void* -Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" @@ -2969,248 +2702,12 @@ Function,+,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, S Function,+,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" Function,+,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" Function,+,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_free,void,void* -Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_smc5326_free,void,void* -Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_star_line_free,void,void* -Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_ansonic_free,void,void* -Function,-,subghz_protocol_encoder_ansonic_stop,void,void* -Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bett_free,void,void* -Function,-,subghz_protocol_encoder_bett_stop,void,void* -Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_atomo_free,void,void* -Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* -Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_free,void,void* -Function,-,subghz_protocol_encoder_came_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_twee_free,void,void* -Function,-,subghz_protocol_encoder_came_twee_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_chamb_code_free,void,void* -Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* -Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_clemsa_free,void,void* -Function,-,subghz_protocol_encoder_clemsa_stop,void,void* -Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_doitrand_free,void,void* -Function,-,subghz_protocol_encoder_doitrand_stop,void,void* -Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_dooya_free,void,void* -Function,+,subghz_protocol_encoder_dooya_stop,void,void* -Function,+,subghz_protocol_encoder_dooya_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_faac_slh_free,void,void* -Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* -Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_gate_tx_free,void,void* -Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* -Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_get_rssi_threshold,int,void* -Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_free,void,void* -Function,-,subghz_protocol_encoder_holtek_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_hormann_free,void,void* -Function,-,subghz_protocol_encoder_hormann_stop,void,void* -Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_keeloq_free,void,void* -Function,-,subghz_protocol_encoder_keeloq_stop,void,void* -Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_linear_delta3_free,void,void* -Function,+,subghz_protocol_encoder_linear_delta3_stop,void,void* -Function,+,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_free,void,void* -Function,-,subghz_protocol_encoder_linear_stop,void,void* -Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellan_free,void,void* -Function,-,subghz_protocol_encoder_magellan_stop,void,void* -Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_marantec_free,void,void* -Function,-,subghz_protocol_encoder_marantec_stop,void,void* -Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_megacode_free,void,void* -Function,-,subghz_protocol_encoder_megacode_stop,void,void* -Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_radio_free,void,void* -Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* -Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flo_free,void,void* -Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_power_smart_free,void,void* -Function,-,subghz_protocol_encoder_power_smart_stop,void,void* -Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_princeton_free,void,void* -Function,+,subghz_protocol_encoder_princeton_stop,void,void* -Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_smc5326_free,void,void* -Function,-,subghz_protocol_encoder_smc5326_stop,void,void* -Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_somfy_keytis_free,void,void* -Function,+,subghz_protocol_encoder_somfy_keytis_stop,void,void* -Function,+,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_somfy_telis_free,void,void* -Function,+,subghz_protocol_encoder_somfy_telis_stop,void,void* -Function,+,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_star_line_free,void,void* -Function,-,subghz_protocol_encoder_star_line_stop,void,void* -Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -3220,11 +2717,6 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" -Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" -Function,+,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,+,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -4934,131 +4426,9 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, -Variable,+,subghz_protocol_alutech_at_4n,const SubGhzProtocol, -Variable,+,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, -Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bett,const SubGhzProtocol, -Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, -Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, -Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, -Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, -Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_dooya,const SubGhzProtocol, -Variable,+,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, -Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, -Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, -Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_hormann,const SubGhzProtocol, -Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ido,const SubGhzProtocol, -Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, -Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, -Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kia,const SubGhzProtocol, -Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, -Variable,+,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_linear_delta3,const SubGhzProtocol, -Variable,+,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellan,const SubGhzProtocol, -Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_marantec,const SubGhzProtocol, -Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_megacode,const SubGhzProtocol, -Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_pocsag,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_princeton,const SubGhzProtocol, -Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, -Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, -Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v1,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v1_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v1_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_smc5326,const SubGhzProtocol, -Variable,-,subghz_protocol_smc5326_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_smc5326_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_keytis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_keytis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_keytis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_telis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_telis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_telis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_star_line,const SubGhzProtocol, -Variable,-,subghz_protocol_star_line_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_star_line_encoder,const SubGhzProtocolEncoder, Variable,-,suboptarg,char*, Variable,-,u8g2_cb_mirror,const u8g2_cb_t, Variable,-,u8g2_cb_r0,const u8g2_cb_t, From 0a6f830ad05cf0be5c61d62eb85fd50c35bdf434 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 20:05:48 +0000 Subject: [PATCH 64/75] Fix api version tag --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a38fe72fe..24c2d32db 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,15.0,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From 955e16b0ef88f120d1c85dc6be24281cf93252a4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:14:40 +0000 Subject: [PATCH 65/75] Move NSFW mode to an asset pack --- .gitignore | 2 + .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 ++- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +-- .../desktop/animations/animation_manager.c | 4 +- .../desktop/animations/animation_storage.c | 10 +-- .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 ---- .../settings/xtreme_settings/xtreme_assets.c | 68 ++++++------------ .../settings/xtreme_settings/xtreme_assets.h | 1 + .../xtreme_settings/xtreme_settings.h | 1 - .../PaxGod_TikTok_Marketing/frame_0.png | Bin .../PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../PaxGod_TikTok_Marketing/frame_3.png | Bin .../PaxGod_TikTok_Marketing/frame_4.png | Bin .../PaxGod_TikTok_Marketing/frame_5.png | Bin .../PaxGod_TikTok_Marketing/frame_6.png | Bin .../PaxGod_TikTok_Marketing/frame_7.png | Bin .../PaxGod_TikTok_Marketing/frame_8.png | Bin .../PaxGod_TikTok_Marketing/frame_9.png | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 0 .../NSFW/Anims}/lvl_1/frame_0.png | Bin .../NSFW/Anims}/lvl_1/frame_1.png | Bin .../NSFW/Anims}/lvl_1/frame_10.png | Bin .../NSFW/Anims}/lvl_1/frame_11.png | Bin .../NSFW/Anims}/lvl_1/frame_12.png | Bin .../NSFW/Anims}/lvl_1/frame_13.png | Bin .../NSFW/Anims}/lvl_1/frame_14.png | Bin .../NSFW/Anims}/lvl_1/frame_15.png | Bin .../NSFW/Anims}/lvl_1/frame_16.png | Bin .../NSFW/Anims}/lvl_1/frame_17.png | Bin .../NSFW/Anims}/lvl_1/frame_18.png | Bin .../NSFW/Anims}/lvl_1/frame_19.png | Bin .../NSFW/Anims}/lvl_1/frame_2.png | Bin .../NSFW/Anims}/lvl_1/frame_20.png | Bin .../NSFW/Anims}/lvl_1/frame_21.png | Bin .../NSFW/Anims}/lvl_1/frame_22.png | Bin .../NSFW/Anims}/lvl_1/frame_23.png | Bin .../NSFW/Anims}/lvl_1/frame_24.png | Bin .../NSFW/Anims}/lvl_1/frame_25.png | Bin .../NSFW/Anims}/lvl_1/frame_26.png | Bin .../NSFW/Anims}/lvl_1/frame_27.png | Bin .../NSFW/Anims}/lvl_1/frame_28.png | Bin .../NSFW/Anims}/lvl_1/frame_29.png | Bin .../NSFW/Anims}/lvl_1/frame_3.png | Bin .../NSFW/Anims}/lvl_1/frame_30.png | Bin .../NSFW/Anims}/lvl_1/frame_4.png | Bin .../NSFW/Anims}/lvl_1/frame_5.png | Bin .../NSFW/Anims}/lvl_1/frame_6.png | Bin .../NSFW/Anims}/lvl_1/frame_7.png | Bin .../NSFW/Anims}/lvl_1/frame_8.png | Bin .../NSFW/Anims}/lvl_1/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.png | Bin .../NSFW/Anims}/lvl_10/frame_1.png | Bin .../NSFW/Anims}/lvl_10/frame_10.png | Bin .../NSFW/Anims}/lvl_10/frame_11.png | Bin .../NSFW/Anims}/lvl_10/frame_12.png | Bin .../NSFW/Anims}/lvl_10/frame_13.png | Bin .../NSFW/Anims}/lvl_10/frame_14.png | Bin .../NSFW/Anims}/lvl_10/frame_15.png | Bin .../NSFW/Anims}/lvl_10/frame_16.png | Bin .../NSFW/Anims}/lvl_10/frame_17.png | Bin .../NSFW/Anims}/lvl_10/frame_18.png | Bin .../NSFW/Anims}/lvl_10/frame_19.png | Bin .../NSFW/Anims}/lvl_10/frame_2.png | Bin .../NSFW/Anims}/lvl_10/frame_20.png | Bin .../NSFW/Anims}/lvl_10/frame_21.png | Bin .../NSFW/Anims}/lvl_10/frame_22.png | Bin .../NSFW/Anims}/lvl_10/frame_23.png | Bin .../NSFW/Anims}/lvl_10/frame_24.png | Bin .../NSFW/Anims}/lvl_10/frame_25.png | Bin .../NSFW/Anims}/lvl_10/frame_26.png | Bin .../NSFW/Anims}/lvl_10/frame_27.png | Bin .../NSFW/Anims}/lvl_10/frame_3.png | Bin .../NSFW/Anims}/lvl_10/frame_4.png | Bin .../NSFW/Anims}/lvl_10/frame_5.png | Bin .../NSFW/Anims}/lvl_10/frame_6.png | Bin .../NSFW/Anims}/lvl_10/frame_7.png | Bin .../NSFW/Anims}/lvl_10/frame_8.png | Bin .../NSFW/Anims}/lvl_10/frame_9.png | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.png | Bin .../NSFW/Anims}/lvl_11/frame_1.png | Bin .../NSFW/Anims}/lvl_11/frame_10.png | Bin .../NSFW/Anims}/lvl_11/frame_11.png | Bin .../NSFW/Anims}/lvl_11/frame_12.png | Bin .../NSFW/Anims}/lvl_11/frame_13.png | Bin .../NSFW/Anims}/lvl_11/frame_14.png | Bin .../NSFW/Anims}/lvl_11/frame_15.png | Bin .../NSFW/Anims}/lvl_11/frame_16.png | Bin .../NSFW/Anims}/lvl_11/frame_17.png | Bin .../NSFW/Anims}/lvl_11/frame_18.png | Bin .../NSFW/Anims}/lvl_11/frame_19.png | Bin .../NSFW/Anims}/lvl_11/frame_2.png | Bin .../NSFW/Anims}/lvl_11/frame_20.png | Bin .../NSFW/Anims}/lvl_11/frame_21.png | Bin .../NSFW/Anims}/lvl_11/frame_22.png | Bin .../NSFW/Anims}/lvl_11/frame_23.png | Bin .../NSFW/Anims}/lvl_11/frame_24.png | Bin .../NSFW/Anims}/lvl_11/frame_25.png | Bin .../NSFW/Anims}/lvl_11/frame_26.png | Bin .../NSFW/Anims}/lvl_11/frame_27.png | Bin .../NSFW/Anims}/lvl_11/frame_28.png | Bin .../NSFW/Anims}/lvl_11/frame_29.png | Bin .../NSFW/Anims}/lvl_11/frame_3.png | Bin .../NSFW/Anims}/lvl_11/frame_30.png | Bin .../NSFW/Anims}/lvl_11/frame_31.png | Bin .../NSFW/Anims}/lvl_11/frame_32.png | Bin .../NSFW/Anims}/lvl_11/frame_33.png | Bin .../NSFW/Anims}/lvl_11/frame_34.png | Bin .../NSFW/Anims}/lvl_11/frame_35.png | Bin .../NSFW/Anims}/lvl_11/frame_36.png | Bin .../NSFW/Anims}/lvl_11/frame_37.png | Bin .../NSFW/Anims}/lvl_11/frame_38.png | Bin .../NSFW/Anims}/lvl_11/frame_39.png | Bin .../NSFW/Anims}/lvl_11/frame_4.png | Bin .../NSFW/Anims}/lvl_11/frame_40.png | Bin .../NSFW/Anims}/lvl_11/frame_41.png | Bin .../NSFW/Anims}/lvl_11/frame_42.png | Bin .../NSFW/Anims}/lvl_11/frame_43.png | Bin .../NSFW/Anims}/lvl_11/frame_44.png | Bin .../NSFW/Anims}/lvl_11/frame_45.png | Bin .../NSFW/Anims}/lvl_11/frame_46.png | Bin .../NSFW/Anims}/lvl_11/frame_47.png | Bin .../NSFW/Anims}/lvl_11/frame_48.png | Bin .../NSFW/Anims}/lvl_11/frame_49.png | Bin .../NSFW/Anims}/lvl_11/frame_5.png | Bin .../NSFW/Anims}/lvl_11/frame_6.png | Bin .../NSFW/Anims}/lvl_11/frame_7.png | Bin .../NSFW/Anims}/lvl_11/frame_8.png | Bin .../NSFW/Anims}/lvl_11/frame_9.png | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.png | Bin .../NSFW/Anims}/lvl_12/frame_1.png | Bin .../NSFW/Anims}/lvl_12/frame_10.png | Bin .../NSFW/Anims}/lvl_12/frame_11.png | Bin .../NSFW/Anims}/lvl_12/frame_12.png | Bin .../NSFW/Anims}/lvl_12/frame_13.png | Bin .../NSFW/Anims}/lvl_12/frame_14.png | Bin .../NSFW/Anims}/lvl_12/frame_15.png | Bin .../NSFW/Anims}/lvl_12/frame_2.png | Bin .../NSFW/Anims}/lvl_12/frame_3.png | Bin .../NSFW/Anims}/lvl_12/frame_4.png | Bin .../NSFW/Anims}/lvl_12/frame_5.png | Bin .../NSFW/Anims}/lvl_12/frame_6.png | Bin .../NSFW/Anims}/lvl_12/frame_7.png | Bin .../NSFW/Anims}/lvl_12/frame_8.png | Bin .../NSFW/Anims}/lvl_12/frame_9.png | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.png | Bin .../NSFW/Anims}/lvl_13/frame_1.png | Bin .../NSFW/Anims}/lvl_13/frame_10.png | Bin .../NSFW/Anims}/lvl_13/frame_2.png | Bin .../NSFW/Anims}/lvl_13/frame_3.png | Bin .../NSFW/Anims}/lvl_13/frame_4.png | Bin .../NSFW/Anims}/lvl_13/frame_5.png | Bin .../NSFW/Anims}/lvl_13/frame_6.png | Bin .../NSFW/Anims}/lvl_13/frame_7.png | Bin .../NSFW/Anims}/lvl_13/frame_8.png | Bin .../NSFW/Anims}/lvl_13/frame_9.png | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.png | Bin .../NSFW/Anims}/lvl_14/frame_1.png | Bin .../NSFW/Anims}/lvl_14/frame_2.png | Bin .../NSFW/Anims}/lvl_14/frame_3.png | Bin .../NSFW/Anims}/lvl_14/frame_4.png | Bin .../NSFW/Anims}/lvl_14/frame_5.png | Bin .../NSFW/Anims}/lvl_14/frame_6.png | Bin .../NSFW/Anims}/lvl_14/frame_7.png | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.png | Bin .../NSFW/Anims}/lvl_15/frame_1.png | Bin .../NSFW/Anims}/lvl_15/frame_10.png | Bin .../NSFW/Anims}/lvl_15/frame_11.png | Bin .../NSFW/Anims}/lvl_15/frame_12.png | Bin .../NSFW/Anims}/lvl_15/frame_13.png | Bin .../NSFW/Anims}/lvl_15/frame_14.png | Bin .../NSFW/Anims}/lvl_15/frame_15.png | Bin .../NSFW/Anims}/lvl_15/frame_16.png | Bin .../NSFW/Anims}/lvl_15/frame_17.png | Bin .../NSFW/Anims}/lvl_15/frame_18.png | Bin .../NSFW/Anims}/lvl_15/frame_19.png | Bin .../NSFW/Anims}/lvl_15/frame_2.png | Bin .../NSFW/Anims}/lvl_15/frame_20.png | Bin .../NSFW/Anims}/lvl_15/frame_21.png | Bin .../NSFW/Anims}/lvl_15/frame_3.png | Bin .../NSFW/Anims}/lvl_15/frame_4.png | Bin .../NSFW/Anims}/lvl_15/frame_5.png | Bin .../NSFW/Anims}/lvl_15/frame_6.png | Bin .../NSFW/Anims}/lvl_15/frame_7.png | Bin .../NSFW/Anims}/lvl_15/frame_8.png | Bin .../NSFW/Anims}/lvl_15/frame_9.png | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.png | Bin .../NSFW/Anims}/lvl_16/frame_1.png | Bin .../NSFW/Anims}/lvl_16/frame_10.png | Bin .../NSFW/Anims}/lvl_16/frame_11.png | Bin .../NSFW/Anims}/lvl_16/frame_12.png | Bin .../NSFW/Anims}/lvl_16/frame_13.png | Bin .../NSFW/Anims}/lvl_16/frame_14.png | Bin .../NSFW/Anims}/lvl_16/frame_15.png | Bin .../NSFW/Anims}/lvl_16/frame_16.png | Bin .../NSFW/Anims}/lvl_16/frame_17.png | Bin .../NSFW/Anims}/lvl_16/frame_18.png | Bin .../NSFW/Anims}/lvl_16/frame_19.png | Bin .../NSFW/Anims}/lvl_16/frame_2.png | Bin .../NSFW/Anims}/lvl_16/frame_20.png | Bin .../NSFW/Anims}/lvl_16/frame_3.png | Bin .../NSFW/Anims}/lvl_16/frame_4.png | Bin .../NSFW/Anims}/lvl_16/frame_5.png | Bin .../NSFW/Anims}/lvl_16/frame_6.png | Bin .../NSFW/Anims}/lvl_16/frame_7.png | Bin .../NSFW/Anims}/lvl_16/frame_8.png | Bin .../NSFW/Anims}/lvl_16/frame_9.png | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.png | Bin .../NSFW/Anims}/lvl_17/frame_1.png | Bin .../NSFW/Anims}/lvl_17/frame_10.png | Bin .../NSFW/Anims}/lvl_17/frame_11.png | Bin .../NSFW/Anims}/lvl_17/frame_12.png | Bin .../NSFW/Anims}/lvl_17/frame_13.png | Bin .../NSFW/Anims}/lvl_17/frame_14.png | Bin .../NSFW/Anims}/lvl_17/frame_15.png | Bin .../NSFW/Anims}/lvl_17/frame_16.png | Bin .../NSFW/Anims}/lvl_17/frame_17.png | Bin .../NSFW/Anims}/lvl_17/frame_18.png | Bin .../NSFW/Anims}/lvl_17/frame_19.png | Bin .../NSFW/Anims}/lvl_17/frame_2.png | Bin .../NSFW/Anims}/lvl_17/frame_20.png | Bin .../NSFW/Anims}/lvl_17/frame_21.png | Bin .../NSFW/Anims}/lvl_17/frame_22.png | Bin .../NSFW/Anims}/lvl_17/frame_23.png | Bin .../NSFW/Anims}/lvl_17/frame_24.png | Bin .../NSFW/Anims}/lvl_17/frame_25.png | Bin .../NSFW/Anims}/lvl_17/frame_26.png | Bin .../NSFW/Anims}/lvl_17/frame_27.png | Bin .../NSFW/Anims}/lvl_17/frame_28.png | Bin .../NSFW/Anims}/lvl_17/frame_29.png | Bin .../NSFW/Anims}/lvl_17/frame_3.png | Bin .../NSFW/Anims}/lvl_17/frame_30.png | Bin .../NSFW/Anims}/lvl_17/frame_31.png | Bin .../NSFW/Anims}/lvl_17/frame_4.png | Bin .../NSFW/Anims}/lvl_17/frame_5.png | Bin .../NSFW/Anims}/lvl_17/frame_6.png | Bin .../NSFW/Anims}/lvl_17/frame_7.png | Bin .../NSFW/Anims}/lvl_17/frame_8.png | Bin .../NSFW/Anims}/lvl_17/frame_9.png | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.png | Bin .../NSFW/Anims}/lvl_18/frame_1.png | Bin .../NSFW/Anims}/lvl_18/frame_10.png | Bin .../NSFW/Anims}/lvl_18/frame_11.png | Bin .../NSFW/Anims}/lvl_18/frame_12.png | Bin .../NSFW/Anims}/lvl_18/frame_13.png | Bin .../NSFW/Anims}/lvl_18/frame_14.png | Bin .../NSFW/Anims}/lvl_18/frame_15.png | Bin .../NSFW/Anims}/lvl_18/frame_16.png | Bin .../NSFW/Anims}/lvl_18/frame_17.png | Bin .../NSFW/Anims}/lvl_18/frame_18.png | Bin .../NSFW/Anims}/lvl_18/frame_19.png | Bin .../NSFW/Anims}/lvl_18/frame_2.png | Bin .../NSFW/Anims}/lvl_18/frame_20.png | Bin .../NSFW/Anims}/lvl_18/frame_21.png | Bin .../NSFW/Anims}/lvl_18/frame_22.png | Bin .../NSFW/Anims}/lvl_18/frame_3.png | Bin .../NSFW/Anims}/lvl_18/frame_4.png | Bin .../NSFW/Anims}/lvl_18/frame_5.png | Bin .../NSFW/Anims}/lvl_18/frame_6.png | Bin .../NSFW/Anims}/lvl_18/frame_7.png | Bin .../NSFW/Anims}/lvl_18/frame_8.png | Bin .../NSFW/Anims}/lvl_18/frame_9.png | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.png | Bin .../NSFW/Anims}/lvl_19/frame_1.png | Bin .../NSFW/Anims}/lvl_19/frame_10.png | Bin .../NSFW/Anims}/lvl_19/frame_11.png | Bin .../NSFW/Anims}/lvl_19/frame_12.png | Bin .../NSFW/Anims}/lvl_19/frame_13.png | Bin .../NSFW/Anims}/lvl_19/frame_14.png | Bin .../NSFW/Anims}/lvl_19/frame_15.png | Bin .../NSFW/Anims}/lvl_19/frame_16.png | Bin .../NSFW/Anims}/lvl_19/frame_17.png | Bin .../NSFW/Anims}/lvl_19/frame_18.png | Bin .../NSFW/Anims}/lvl_19/frame_19.png | Bin .../NSFW/Anims}/lvl_19/frame_2.png | Bin .../NSFW/Anims}/lvl_19/frame_20.png | Bin .../NSFW/Anims}/lvl_19/frame_21.png | Bin .../NSFW/Anims}/lvl_19/frame_3.png | Bin .../NSFW/Anims}/lvl_19/frame_4.png | Bin .../NSFW/Anims}/lvl_19/frame_5.png | Bin .../NSFW/Anims}/lvl_19/frame_6.png | Bin .../NSFW/Anims}/lvl_19/frame_7.png | Bin .../NSFW/Anims}/lvl_19/frame_8.png | Bin .../NSFW/Anims}/lvl_19/frame_9.png | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.png | Bin .../NSFW/Anims}/lvl_2/frame_1.png | Bin .../NSFW/Anims}/lvl_2/frame_10.png | Bin .../NSFW/Anims}/lvl_2/frame_11.png | Bin .../NSFW/Anims}/lvl_2/frame_12.png | Bin .../NSFW/Anims}/lvl_2/frame_13.png | Bin .../NSFW/Anims}/lvl_2/frame_14.png | Bin .../NSFW/Anims}/lvl_2/frame_2.png | Bin .../NSFW/Anims}/lvl_2/frame_3.png | Bin .../NSFW/Anims}/lvl_2/frame_4.png | Bin .../NSFW/Anims}/lvl_2/frame_5.png | Bin .../NSFW/Anims}/lvl_2/frame_6.png | Bin .../NSFW/Anims}/lvl_2/frame_7.png | Bin .../NSFW/Anims}/lvl_2/frame_8.png | Bin .../NSFW/Anims}/lvl_2/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.png | Bin .../NSFW/Anims}/lvl_20/frame_1.png | Bin .../NSFW/Anims}/lvl_20/frame_10.png | Bin .../NSFW/Anims}/lvl_20/frame_11.png | Bin .../NSFW/Anims}/lvl_20/frame_12.png | Bin .../NSFW/Anims}/lvl_20/frame_13.png | Bin .../NSFW/Anims}/lvl_20/frame_2.png | Bin .../NSFW/Anims}/lvl_20/frame_3.png | Bin .../NSFW/Anims}/lvl_20/frame_4.png | Bin .../NSFW/Anims}/lvl_20/frame_5.png | Bin .../NSFW/Anims}/lvl_20/frame_6.png | Bin .../NSFW/Anims}/lvl_20/frame_7.png | Bin .../NSFW/Anims}/lvl_20/frame_8.png | Bin .../NSFW/Anims}/lvl_20/frame_9.png | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.png | Bin .../NSFW/Anims}/lvl_21/frame_1.png | Bin .../NSFW/Anims}/lvl_21/frame_2.png | Bin .../NSFW/Anims}/lvl_21/frame_3.png | Bin .../NSFW/Anims}/lvl_21/frame_4.png | Bin .../NSFW/Anims}/lvl_21/frame_5.png | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.png | Bin .../NSFW/Anims}/lvl_22/frame_1.png | Bin .../NSFW/Anims}/lvl_22/frame_10.png | Bin .../NSFW/Anims}/lvl_22/frame_11.png | Bin .../NSFW/Anims}/lvl_22/frame_12.png | Bin .../NSFW/Anims}/lvl_22/frame_13.png | Bin .../NSFW/Anims}/lvl_22/frame_14.png | Bin .../NSFW/Anims}/lvl_22/frame_15.png | Bin .../NSFW/Anims}/lvl_22/frame_16.png | Bin .../NSFW/Anims}/lvl_22/frame_17.png | Bin .../NSFW/Anims}/lvl_22/frame_18.png | Bin .../NSFW/Anims}/lvl_22/frame_19.png | Bin .../NSFW/Anims}/lvl_22/frame_2.png | Bin .../NSFW/Anims}/lvl_22/frame_20.png | Bin .../NSFW/Anims}/lvl_22/frame_21.png | Bin .../NSFW/Anims}/lvl_22/frame_22.png | Bin .../NSFW/Anims}/lvl_22/frame_23.png | Bin .../NSFW/Anims}/lvl_22/frame_24.png | Bin .../NSFW/Anims}/lvl_22/frame_25.png | Bin .../NSFW/Anims}/lvl_22/frame_26.png | Bin .../NSFW/Anims}/lvl_22/frame_27.png | Bin .../NSFW/Anims}/lvl_22/frame_28.png | Bin .../NSFW/Anims}/lvl_22/frame_29.png | Bin .../NSFW/Anims}/lvl_22/frame_3.png | Bin .../NSFW/Anims}/lvl_22/frame_30.png | Bin .../NSFW/Anims}/lvl_22/frame_31.png | Bin .../NSFW/Anims}/lvl_22/frame_32.png | Bin .../NSFW/Anims}/lvl_22/frame_33.png | Bin .../NSFW/Anims}/lvl_22/frame_34.png | Bin .../NSFW/Anims}/lvl_22/frame_35.png | Bin .../NSFW/Anims}/lvl_22/frame_36.png | Bin .../NSFW/Anims}/lvl_22/frame_37.png | Bin .../NSFW/Anims}/lvl_22/frame_38.png | Bin .../NSFW/Anims}/lvl_22/frame_39.png | Bin .../NSFW/Anims}/lvl_22/frame_4.png | Bin .../NSFW/Anims}/lvl_22/frame_40.png | Bin .../NSFW/Anims}/lvl_22/frame_41.png | Bin .../NSFW/Anims}/lvl_22/frame_42.png | Bin .../NSFW/Anims}/lvl_22/frame_43.png | Bin .../NSFW/Anims}/lvl_22/frame_44.png | Bin .../NSFW/Anims}/lvl_22/frame_45.png | Bin .../NSFW/Anims}/lvl_22/frame_46.png | Bin .../NSFW/Anims}/lvl_22/frame_47.png | Bin .../NSFW/Anims}/lvl_22/frame_48.png | Bin .../NSFW/Anims}/lvl_22/frame_49.png | Bin .../NSFW/Anims}/lvl_22/frame_5.png | Bin .../NSFW/Anims}/lvl_22/frame_50.png | Bin .../NSFW/Anims}/lvl_22/frame_51.png | Bin .../NSFW/Anims}/lvl_22/frame_52.png | Bin .../NSFW/Anims}/lvl_22/frame_53.png | Bin .../NSFW/Anims}/lvl_22/frame_54.png | Bin .../NSFW/Anims}/lvl_22/frame_55.png | Bin .../NSFW/Anims}/lvl_22/frame_56.png | Bin .../NSFW/Anims}/lvl_22/frame_57.png | Bin .../NSFW/Anims}/lvl_22/frame_58.png | Bin .../NSFW/Anims}/lvl_22/frame_59.png | Bin .../NSFW/Anims}/lvl_22/frame_6.png | Bin .../NSFW/Anims}/lvl_22/frame_7.png | Bin .../NSFW/Anims}/lvl_22/frame_8.png | Bin .../NSFW/Anims}/lvl_22/frame_9.png | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.png | Bin .../NSFW/Anims}/lvl_23/frame_1.png | Bin .../NSFW/Anims}/lvl_23/frame_10.png | Bin .../NSFW/Anims}/lvl_23/frame_11.png | Bin .../NSFW/Anims}/lvl_23/frame_12.png | Bin .../NSFW/Anims}/lvl_23/frame_13.png | Bin .../NSFW/Anims}/lvl_23/frame_14.png | Bin .../NSFW/Anims}/lvl_23/frame_15.png | Bin .../NSFW/Anims}/lvl_23/frame_16.png | Bin .../NSFW/Anims}/lvl_23/frame_2.png | Bin .../NSFW/Anims}/lvl_23/frame_3.png | Bin .../NSFW/Anims}/lvl_23/frame_4.png | Bin .../NSFW/Anims}/lvl_23/frame_5.png | Bin .../NSFW/Anims}/lvl_23/frame_6.png | Bin .../NSFW/Anims}/lvl_23/frame_7.png | Bin .../NSFW/Anims}/lvl_23/frame_8.png | Bin .../NSFW/Anims}/lvl_23/frame_9.png | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.png | Bin .../NSFW/Anims}/lvl_24/frame_1.png | Bin .../NSFW/Anims}/lvl_24/frame_10.png | Bin .../NSFW/Anims}/lvl_24/frame_11.png | Bin .../NSFW/Anims}/lvl_24/frame_12.png | Bin .../NSFW/Anims}/lvl_24/frame_13.png | Bin .../NSFW/Anims}/lvl_24/frame_14.png | Bin .../NSFW/Anims}/lvl_24/frame_15.png | Bin .../NSFW/Anims}/lvl_24/frame_16.png | Bin .../NSFW/Anims}/lvl_24/frame_17.png | Bin .../NSFW/Anims}/lvl_24/frame_18.png | Bin .../NSFW/Anims}/lvl_24/frame_19.png | Bin .../NSFW/Anims}/lvl_24/frame_2.png | Bin .../NSFW/Anims}/lvl_24/frame_20.png | Bin .../NSFW/Anims}/lvl_24/frame_21.png | Bin .../NSFW/Anims}/lvl_24/frame_22.png | Bin .../NSFW/Anims}/lvl_24/frame_23.png | Bin .../NSFW/Anims}/lvl_24/frame_24.png | Bin .../NSFW/Anims}/lvl_24/frame_25.png | Bin .../NSFW/Anims}/lvl_24/frame_26.png | Bin .../NSFW/Anims}/lvl_24/frame_27.png | Bin .../NSFW/Anims}/lvl_24/frame_28.png | Bin .../NSFW/Anims}/lvl_24/frame_29.png | Bin .../NSFW/Anims}/lvl_24/frame_3.png | Bin .../NSFW/Anims}/lvl_24/frame_4.png | Bin .../NSFW/Anims}/lvl_24/frame_5.png | Bin .../NSFW/Anims}/lvl_24/frame_6.png | Bin .../NSFW/Anims}/lvl_24/frame_7.png | Bin .../NSFW/Anims}/lvl_24/frame_8.png | Bin .../NSFW/Anims}/lvl_24/frame_9.png | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.png | Bin .../NSFW/Anims}/lvl_25/frame_1.png | Bin .../NSFW/Anims}/lvl_25/frame_10.png | Bin .../NSFW/Anims}/lvl_25/frame_11.png | Bin .../NSFW/Anims}/lvl_25/frame_12.png | Bin .../NSFW/Anims}/lvl_25/frame_13.png | Bin .../NSFW/Anims}/lvl_25/frame_14.png | Bin .../NSFW/Anims}/lvl_25/frame_15.png | Bin .../NSFW/Anims}/lvl_25/frame_16.png | Bin .../NSFW/Anims}/lvl_25/frame_17.png | Bin .../NSFW/Anims}/lvl_25/frame_18.png | Bin .../NSFW/Anims}/lvl_25/frame_19.png | Bin .../NSFW/Anims}/lvl_25/frame_2.png | Bin .../NSFW/Anims}/lvl_25/frame_20.png | Bin .../NSFW/Anims}/lvl_25/frame_21.png | Bin .../NSFW/Anims}/lvl_25/frame_22.png | Bin .../NSFW/Anims}/lvl_25/frame_23.png | Bin .../NSFW/Anims}/lvl_25/frame_24.png | Bin .../NSFW/Anims}/lvl_25/frame_25.png | Bin .../NSFW/Anims}/lvl_25/frame_26.png | Bin .../NSFW/Anims}/lvl_25/frame_27.png | Bin .../NSFW/Anims}/lvl_25/frame_28.png | Bin .../NSFW/Anims}/lvl_25/frame_29.png | Bin .../NSFW/Anims}/lvl_25/frame_3.png | Bin .../NSFW/Anims}/lvl_25/frame_30.png | Bin .../NSFW/Anims}/lvl_25/frame_31.png | Bin .../NSFW/Anims}/lvl_25/frame_32.png | Bin .../NSFW/Anims}/lvl_25/frame_33.png | Bin .../NSFW/Anims}/lvl_25/frame_34.png | Bin .../NSFW/Anims}/lvl_25/frame_35.png | Bin .../NSFW/Anims}/lvl_25/frame_4.png | Bin .../NSFW/Anims}/lvl_25/frame_5.png | Bin .../NSFW/Anims}/lvl_25/frame_6.png | Bin .../NSFW/Anims}/lvl_25/frame_7.png | Bin .../NSFW/Anims}/lvl_25/frame_8.png | Bin .../NSFW/Anims}/lvl_25/frame_9.png | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.png | Bin .../NSFW/Anims}/lvl_26/frame_1.png | Bin .../NSFW/Anims}/lvl_26/frame_10.png | Bin .../NSFW/Anims}/lvl_26/frame_11.png | Bin .../NSFW/Anims}/lvl_26/frame_2.png | Bin .../NSFW/Anims}/lvl_26/frame_3.png | Bin .../NSFW/Anims}/lvl_26/frame_4.png | Bin .../NSFW/Anims}/lvl_26/frame_5.png | Bin .../NSFW/Anims}/lvl_26/frame_6.png | Bin .../NSFW/Anims}/lvl_26/frame_7.png | Bin .../NSFW/Anims}/lvl_26/frame_8.png | Bin .../NSFW/Anims}/lvl_26/frame_9.png | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.png | Bin .../NSFW/Anims}/lvl_27/frame_1.png | Bin .../NSFW/Anims}/lvl_27/frame_10.png | Bin .../NSFW/Anims}/lvl_27/frame_11.png | Bin .../NSFW/Anims}/lvl_27/frame_12.png | Bin .../NSFW/Anims}/lvl_27/frame_13.png | Bin .../NSFW/Anims}/lvl_27/frame_14.png | Bin .../NSFW/Anims}/lvl_27/frame_15.png | Bin .../NSFW/Anims}/lvl_27/frame_16.png | Bin .../NSFW/Anims}/lvl_27/frame_17.png | Bin .../NSFW/Anims}/lvl_27/frame_18.png | Bin .../NSFW/Anims}/lvl_27/frame_19.png | Bin .../NSFW/Anims}/lvl_27/frame_2.png | Bin .../NSFW/Anims}/lvl_27/frame_20.png | Bin .../NSFW/Anims}/lvl_27/frame_21.png | Bin .../NSFW/Anims}/lvl_27/frame_3.png | Bin .../NSFW/Anims}/lvl_27/frame_4.png | Bin .../NSFW/Anims}/lvl_27/frame_5.png | Bin .../NSFW/Anims}/lvl_27/frame_6.png | Bin .../NSFW/Anims}/lvl_27/frame_7.png | Bin .../NSFW/Anims}/lvl_27/frame_8.png | Bin .../NSFW/Anims}/lvl_27/frame_9.png | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.png | Bin .../NSFW/Anims}/lvl_28/frame_1.png | Bin .../NSFW/Anims}/lvl_28/frame_2.png | Bin .../NSFW/Anims}/lvl_28/frame_3.png | Bin .../NSFW/Anims}/lvl_28/frame_4.png | Bin .../NSFW/Anims}/lvl_28/frame_5.png | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.png | Bin .../NSFW/Anims}/lvl_29/frame_1.png | Bin .../NSFW/Anims}/lvl_29/frame_10.png | Bin .../NSFW/Anims}/lvl_29/frame_11.png | Bin .../NSFW/Anims}/lvl_29/frame_12.png | Bin .../NSFW/Anims}/lvl_29/frame_13.png | Bin .../NSFW/Anims}/lvl_29/frame_14.png | Bin .../NSFW/Anims}/lvl_29/frame_15.png | Bin .../NSFW/Anims}/lvl_29/frame_16.png | Bin .../NSFW/Anims}/lvl_29/frame_17.png | Bin .../NSFW/Anims}/lvl_29/frame_18.png | Bin .../NSFW/Anims}/lvl_29/frame_19.png | Bin .../NSFW/Anims}/lvl_29/frame_2.png | Bin .../NSFW/Anims}/lvl_29/frame_20.png | Bin .../NSFW/Anims}/lvl_29/frame_21.png | Bin .../NSFW/Anims}/lvl_29/frame_22.png | Bin .../NSFW/Anims}/lvl_29/frame_23.png | Bin .../NSFW/Anims}/lvl_29/frame_24.png | Bin .../NSFW/Anims}/lvl_29/frame_25.png | Bin .../NSFW/Anims}/lvl_29/frame_26.png | Bin .../NSFW/Anims}/lvl_29/frame_27.png | Bin .../NSFW/Anims}/lvl_29/frame_28.png | Bin .../NSFW/Anims}/lvl_29/frame_29.png | Bin .../NSFW/Anims}/lvl_29/frame_3.png | Bin .../NSFW/Anims}/lvl_29/frame_30.png | Bin .../NSFW/Anims}/lvl_29/frame_31.png | Bin .../NSFW/Anims}/lvl_29/frame_32.png | Bin .../NSFW/Anims}/lvl_29/frame_33.png | Bin .../NSFW/Anims}/lvl_29/frame_34.png | Bin .../NSFW/Anims}/lvl_29/frame_35.png | Bin .../NSFW/Anims}/lvl_29/frame_36.png | Bin .../NSFW/Anims}/lvl_29/frame_37.png | Bin .../NSFW/Anims}/lvl_29/frame_38.png | Bin .../NSFW/Anims}/lvl_29/frame_39.png | Bin .../NSFW/Anims}/lvl_29/frame_4.png | Bin .../NSFW/Anims}/lvl_29/frame_40.png | Bin .../NSFW/Anims}/lvl_29/frame_41.png | Bin .../NSFW/Anims}/lvl_29/frame_42.png | Bin .../NSFW/Anims}/lvl_29/frame_43.png | Bin .../NSFW/Anims}/lvl_29/frame_44.png | Bin .../NSFW/Anims}/lvl_29/frame_45.png | Bin .../NSFW/Anims}/lvl_29/frame_46.png | Bin .../NSFW/Anims}/lvl_29/frame_47.png | Bin .../NSFW/Anims}/lvl_29/frame_48.png | Bin .../NSFW/Anims}/lvl_29/frame_49.png | Bin .../NSFW/Anims}/lvl_29/frame_5.png | Bin .../NSFW/Anims}/lvl_29/frame_50.png | Bin .../NSFW/Anims}/lvl_29/frame_51.png | Bin .../NSFW/Anims}/lvl_29/frame_6.png | Bin .../NSFW/Anims}/lvl_29/frame_7.png | Bin .../NSFW/Anims}/lvl_29/frame_8.png | Bin .../NSFW/Anims}/lvl_29/frame_9.png | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.png | Bin .../NSFW/Anims}/lvl_3/frame_1.png | Bin .../NSFW/Anims}/lvl_3/frame_10.png | Bin .../NSFW/Anims}/lvl_3/frame_11.png | Bin .../NSFW/Anims}/lvl_3/frame_12.png | Bin .../NSFW/Anims}/lvl_3/frame_13.png | Bin .../NSFW/Anims}/lvl_3/frame_14.png | Bin .../NSFW/Anims}/lvl_3/frame_2.png | Bin .../NSFW/Anims}/lvl_3/frame_3.png | Bin .../NSFW/Anims}/lvl_3/frame_4.png | Bin .../NSFW/Anims}/lvl_3/frame_5.png | Bin .../NSFW/Anims}/lvl_3/frame_6.png | Bin .../NSFW/Anims}/lvl_3/frame_7.png | Bin .../NSFW/Anims}/lvl_3/frame_8.png | Bin .../NSFW/Anims}/lvl_3/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.png | Bin .../NSFW/Anims}/lvl_30/frame_1.png | Bin .../NSFW/Anims}/lvl_30/frame_10.png | Bin .../NSFW/Anims}/lvl_30/frame_11.png | Bin .../NSFW/Anims}/lvl_30/frame_12.png | Bin .../NSFW/Anims}/lvl_30/frame_13.png | Bin .../NSFW/Anims}/lvl_30/frame_14.png | Bin .../NSFW/Anims}/lvl_30/frame_15.png | Bin .../NSFW/Anims}/lvl_30/frame_16.png | Bin .../NSFW/Anims}/lvl_30/frame_17.png | Bin .../NSFW/Anims}/lvl_30/frame_18.png | Bin .../NSFW/Anims}/lvl_30/frame_19.png | Bin .../NSFW/Anims}/lvl_30/frame_2.png | Bin .../NSFW/Anims}/lvl_30/frame_20.png | Bin .../NSFW/Anims}/lvl_30/frame_21.png | Bin .../NSFW/Anims}/lvl_30/frame_22.png | Bin .../NSFW/Anims}/lvl_30/frame_23.png | Bin .../NSFW/Anims}/lvl_30/frame_24.png | Bin .../NSFW/Anims}/lvl_30/frame_25.png | Bin .../NSFW/Anims}/lvl_30/frame_26.png | Bin .../NSFW/Anims}/lvl_30/frame_27.png | Bin .../NSFW/Anims}/lvl_30/frame_28.png | Bin .../NSFW/Anims}/lvl_30/frame_29.png | Bin .../NSFW/Anims}/lvl_30/frame_3.png | Bin .../NSFW/Anims}/lvl_30/frame_30.png | Bin .../NSFW/Anims}/lvl_30/frame_31.png | Bin .../NSFW/Anims}/lvl_30/frame_32.png | Bin .../NSFW/Anims}/lvl_30/frame_33.png | Bin .../NSFW/Anims}/lvl_30/frame_34.png | Bin .../NSFW/Anims}/lvl_30/frame_35.png | Bin .../NSFW/Anims}/lvl_30/frame_36.png | Bin .../NSFW/Anims}/lvl_30/frame_37.png | Bin .../NSFW/Anims}/lvl_30/frame_38.png | Bin .../NSFW/Anims}/lvl_30/frame_39.png | Bin .../NSFW/Anims}/lvl_30/frame_4.png | Bin .../NSFW/Anims}/lvl_30/frame_40.png | Bin .../NSFW/Anims}/lvl_30/frame_41.png | Bin .../NSFW/Anims}/lvl_30/frame_42.png | Bin .../NSFW/Anims}/lvl_30/frame_43.png | Bin .../NSFW/Anims}/lvl_30/frame_44.png | Bin .../NSFW/Anims}/lvl_30/frame_45.png | Bin .../NSFW/Anims}/lvl_30/frame_46.png | Bin .../NSFW/Anims}/lvl_30/frame_47.png | Bin .../NSFW/Anims}/lvl_30/frame_48.png | Bin .../NSFW/Anims}/lvl_30/frame_49.png | Bin .../NSFW/Anims}/lvl_30/frame_5.png | Bin .../NSFW/Anims}/lvl_30/frame_6.png | Bin .../NSFW/Anims}/lvl_30/frame_7.png | Bin .../NSFW/Anims}/lvl_30/frame_8.png | Bin .../NSFW/Anims}/lvl_30/frame_9.png | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.png | Bin .../NSFW/Anims}/lvl_4/frame_1.png | Bin .../NSFW/Anims}/lvl_4/frame_10.png | Bin .../NSFW/Anims}/lvl_4/frame_11.png | Bin .../NSFW/Anims}/lvl_4/frame_12.png | Bin .../NSFW/Anims}/lvl_4/frame_13.png | Bin .../NSFW/Anims}/lvl_4/frame_14.png | Bin .../NSFW/Anims}/lvl_4/frame_15.png | Bin .../NSFW/Anims}/lvl_4/frame_16.png | Bin .../NSFW/Anims}/lvl_4/frame_17.png | Bin .../NSFW/Anims}/lvl_4/frame_18.png | Bin .../NSFW/Anims}/lvl_4/frame_19.png | Bin .../NSFW/Anims}/lvl_4/frame_2.png | Bin .../NSFW/Anims}/lvl_4/frame_3.png | Bin .../NSFW/Anims}/lvl_4/frame_4.png | Bin .../NSFW/Anims}/lvl_4/frame_5.png | Bin .../NSFW/Anims}/lvl_4/frame_6.png | Bin .../NSFW/Anims}/lvl_4/frame_7.png | Bin .../NSFW/Anims}/lvl_4/frame_8.png | Bin .../NSFW/Anims}/lvl_4/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.png | Bin .../NSFW/Anims}/lvl_5/frame_1.png | Bin .../NSFW/Anims}/lvl_5/frame_10.png | Bin .../NSFW/Anims}/lvl_5/frame_11.png | Bin .../NSFW/Anims}/lvl_5/frame_12.png | Bin .../NSFW/Anims}/lvl_5/frame_13.png | Bin .../NSFW/Anims}/lvl_5/frame_14.png | Bin .../NSFW/Anims}/lvl_5/frame_15.png | Bin .../NSFW/Anims}/lvl_5/frame_16.png | Bin .../NSFW/Anims}/lvl_5/frame_17.png | Bin .../NSFW/Anims}/lvl_5/frame_18.png | Bin .../NSFW/Anims}/lvl_5/frame_19.png | Bin .../NSFW/Anims}/lvl_5/frame_2.png | Bin .../NSFW/Anims}/lvl_5/frame_20.png | Bin .../NSFW/Anims}/lvl_5/frame_21.png | Bin .../NSFW/Anims}/lvl_5/frame_22.png | Bin .../NSFW/Anims}/lvl_5/frame_23.png | Bin .../NSFW/Anims}/lvl_5/frame_24.png | Bin .../NSFW/Anims}/lvl_5/frame_25.png | Bin .../NSFW/Anims}/lvl_5/frame_26.png | Bin .../NSFW/Anims}/lvl_5/frame_27.png | Bin .../NSFW/Anims}/lvl_5/frame_3.png | Bin .../NSFW/Anims}/lvl_5/frame_4.png | Bin .../NSFW/Anims}/lvl_5/frame_5.png | Bin .../NSFW/Anims}/lvl_5/frame_6.png | Bin .../NSFW/Anims}/lvl_5/frame_7.png | Bin .../NSFW/Anims}/lvl_5/frame_8.png | Bin .../NSFW/Anims}/lvl_5/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.png | Bin .../NSFW/Anims}/lvl_6/frame_1.png | Bin .../NSFW/Anims}/lvl_6/frame_2.png | Bin .../NSFW/Anims}/lvl_6/frame_3.png | Bin .../NSFW/Anims}/lvl_6/frame_4.png | Bin .../NSFW/Anims}/lvl_6/frame_5.png | Bin .../NSFW/Anims}/lvl_6/frame_6.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.png | Bin .../NSFW/Anims}/lvl_7/frame_1.png | Bin .../NSFW/Anims}/lvl_7/frame_10.png | Bin .../NSFW/Anims}/lvl_7/frame_11.png | Bin .../NSFW/Anims}/lvl_7/frame_12.png | Bin .../NSFW/Anims}/lvl_7/frame_13.png | Bin .../NSFW/Anims}/lvl_7/frame_2.png | Bin .../NSFW/Anims}/lvl_7/frame_3.png | Bin .../NSFW/Anims}/lvl_7/frame_4.png | Bin .../NSFW/Anims}/lvl_7/frame_5.png | Bin .../NSFW/Anims}/lvl_7/frame_6.png | Bin .../NSFW/Anims}/lvl_7/frame_7.png | Bin .../NSFW/Anims}/lvl_7/frame_8.png | Bin .../NSFW/Anims}/lvl_7/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_7/meta.txt | 0 .../NSFW/Anims}/lvl_8/frame_0.png | Bin .../NSFW/Anims}/lvl_8/frame_1.png | Bin .../NSFW/Anims}/lvl_8/frame_2.png | Bin .../NSFW/Anims}/lvl_8/frame_3.png | Bin .../NSFW/Anims}/lvl_8/frame_4.png | Bin .../NSFW/Anims}/lvl_8/frame_5.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.png | Bin .../NSFW/Anims}/lvl_9/frame_1.png | Bin .../NSFW/Anims}/lvl_9/frame_2.png | Bin .../NSFW/Anims}/lvl_9/frame_3.png | Bin .../NSFW/Anims}/lvl_9/frame_4.png | Bin .../NSFW/Anims}/lvl_9/frame_5.png | Bin .../NSFW/Anims}/lvl_9/frame_6.png | Bin .../NSFW/Anims}/lvl_9/frame_7.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_9/meta.txt | 0 .../nsfw => custom/NSFW/Anims}/manifest.txt | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 0 -> 2610 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 0 -> 3376 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 0 -> 5390 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 0 -> 4224 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 0 -> 852 bytes .../Icons/Passport/passport_bad_46x49.png} | Bin .../Icons/Passport/passport_happy_46x49.png | Bin 0 -> 6373 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 0 -> 6373 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 0 -> 4862 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 0 -> 4882 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 0 -> 4466 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 0 -> 3798 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 0 -> 4092 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 0 -> 1864 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 0 -> 1895 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 0 -> 1874 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 0 -> 1863 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 0 -> 6876 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 0 -> 5422 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 0 -> 5122 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 0 -> 4719 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.png | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.png | Bin .../{sfw => }/L1_Cry_128x64/frame_1.png | Bin .../{sfw => }/L1_Cry_128x64/frame_2.png | Bin .../{sfw => }/L1_Cry_128x64/frame_3.png | Bin .../{sfw => }/L1_Cry_128x64/frame_4.png | Bin .../{sfw => }/L1_Cry_128x64/frame_5.png | Bin .../{sfw => }/L1_Cry_128x64/frame_6.png | Bin .../{sfw => }/L1_Cry_128x64/frame_7.png | Bin .../external/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.png | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.png | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.png | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.png | Bin .../{sfw => }/L1_Mods_128x64/frame_1.png | Bin .../{sfw => }/L1_Mods_128x64/frame_10.png | Bin .../{sfw => }/L1_Mods_128x64/frame_11.png | Bin .../{sfw => }/L1_Mods_128x64/frame_12.png | Bin .../{sfw => }/L1_Mods_128x64/frame_13.png | Bin .../{sfw => }/L1_Mods_128x64/frame_14.png | Bin .../{sfw => }/L1_Mods_128x64/frame_15.png | Bin .../{sfw => }/L1_Mods_128x64/frame_16.png | Bin .../{sfw => }/L1_Mods_128x64/frame_17.png | Bin .../{sfw => }/L1_Mods_128x64/frame_18.png | Bin .../{sfw => }/L1_Mods_128x64/frame_19.png | Bin .../{sfw => }/L1_Mods_128x64/frame_2.png | Bin .../{sfw => }/L1_Mods_128x64/frame_20.png | Bin .../{sfw => }/L1_Mods_128x64/frame_21.png | Bin .../{sfw => }/L1_Mods_128x64/frame_22.png | Bin .../{sfw => }/L1_Mods_128x64/frame_23.png | Bin .../{sfw => }/L1_Mods_128x64/frame_24.png | Bin .../{sfw => }/L1_Mods_128x64/frame_25.png | Bin .../{sfw => }/L1_Mods_128x64/frame_26.png | Bin .../{sfw => }/L1_Mods_128x64/frame_27.png | Bin .../{sfw => }/L1_Mods_128x64/frame_28.png | Bin .../{sfw => }/L1_Mods_128x64/frame_29.png | Bin .../{sfw => }/L1_Mods_128x64/frame_3.png | Bin .../{sfw => }/L1_Mods_128x64/frame_30.png | Bin .../{sfw => }/L1_Mods_128x64/frame_31.png | Bin .../{sfw => }/L1_Mods_128x64/frame_32.png | Bin .../{sfw => }/L1_Mods_128x64/frame_33.png | Bin .../{sfw => }/L1_Mods_128x64/frame_34.png | Bin .../{sfw => }/L1_Mods_128x64/frame_35.png | Bin .../{sfw => }/L1_Mods_128x64/frame_36.png | Bin .../{sfw => }/L1_Mods_128x64/frame_37.png | Bin .../{sfw => }/L1_Mods_128x64/frame_38.png | Bin .../{sfw => }/L1_Mods_128x64/frame_39.png | Bin .../{sfw => }/L1_Mods_128x64/frame_4.png | Bin .../{sfw => }/L1_Mods_128x64/frame_40.png | Bin .../{sfw => }/L1_Mods_128x64/frame_5.png | Bin .../{sfw => }/L1_Mods_128x64/frame_6.png | Bin .../{sfw => }/L1_Mods_128x64/frame_7.png | Bin .../{sfw => }/L1_Mods_128x64/frame_8.png | Bin .../{sfw => }/L1_Mods_128x64/frame_9.png | Bin .../{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.png | Bin .../{sfw => }/L1_Painting_128x64/frame_1.png | Bin .../{sfw => }/L1_Painting_128x64/frame_10.png | Bin .../{sfw => }/L1_Painting_128x64/frame_11.png | Bin .../{sfw => }/L1_Painting_128x64/frame_2.png | Bin .../{sfw => }/L1_Painting_128x64/frame_3.png | Bin .../{sfw => }/L1_Painting_128x64/frame_4.png | Bin .../{sfw => }/L1_Painting_128x64/frame_5.png | Bin .../{sfw => }/L1_Painting_128x64/frame_6.png | Bin .../{sfw => }/L1_Painting_128x64/frame_7.png | Bin .../{sfw => }/L1_Painting_128x64/frame_8.png | Bin .../{sfw => }/L1_Painting_128x64/frame_9.png | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.png | Bin .../{sfw => }/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{sfw => }/L1_Recording_128x51/frame_2.png | Bin .../{sfw => }/L1_Recording_128x51/frame_3.png | Bin .../{sfw => }/L1_Recording_128x51/frame_4.png | Bin .../{sfw => }/L1_Recording_128x51/frame_5.png | Bin .../{sfw => }/L1_Recording_128x51/frame_6.png | Bin .../{sfw => }/L1_Recording_128x51/frame_7.png | Bin .../{sfw => }/L1_Recording_128x51/frame_8.png | Bin .../{sfw => }/L1_Recording_128x51/frame_9.png | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.png | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.png | Bin .../{sfw => }/L1_Waves_128x50/frame_1.png | Bin .../{sfw => }/L1_Waves_128x50/frame_2.png | Bin .../{sfw => }/L1_Waves_128x50/frame_3.png | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.png | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.png | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.png | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.png | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{sfw => }/manifest.txt | 0 assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 2307 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 2307 -> 0 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 1416 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 1416 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 1177 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 1177 -> 0 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 1541 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 1541 -> 0 bytes assets/icons/Passport/passport_DB.png | Bin 852 -> 750 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 750 -> 0 bytes .../Passport/passport_bad2_46x49_sfw.png | Bin 1295 -> 0 bytes .../Passport/passport_bad3_46x49_sfw.png | Bin 1304 -> 0 bytes ...1_46x49_sfw.png => passport_bad_46x49.png} | Bin .../Passport/passport_happy2_46x49_sfw.png | Bin 1328 -> 0 bytes .../Passport/passport_happy3_46x49_sfw.png | Bin 1348 -> 0 bytes ...46x49_sfw.png => passport_happy_46x49.png} | Bin .../Passport/passport_okay2_46x49_sfw.png | Bin 1281 -> 0 bytes .../Passport/passport_okay3_46x49_sfw.png | Bin 1304 -> 0 bytes ..._46x49_sfw.png => passport_okay_46x49.png} | Bin .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 1421 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 1421 -> 0 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 1418 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 1418 -> 0 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 2681 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 2681 -> 0 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 3898 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 3898 -> 0 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 1690 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 1690 -> 0 bytes assets/icons/U2F/Auth_62x31.png | Bin 1864 -> 3761 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 3761 -> 0 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 1895 -> 3767 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 3767 -> 0 bytes assets/icons/U2F/Connected_62x31.png | Bin 1874 -> 3765 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 3765 -> 0 bytes assets/icons/U2F/Error_62x31.png | Bin 1863 -> 3751 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 3751 -> 0 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 2504 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 2504 -> 0 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 2459 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 2459 -> 0 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 2023 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 2023 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 2157 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 2157 -> 0 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.bm | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_1.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_2.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_3.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_4.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_5.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_6.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.bm | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.bm | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_14.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_15.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_16.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_17.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_18.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_19.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_20.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_21.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_22.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_23.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_24.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_25.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_26.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_27.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_28.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_29.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_30.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_31.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_32.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_33.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_34.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_35.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_36.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_37.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_38.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_39.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_40.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_1.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_10.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_11.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_2.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_3.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_4.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_5.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_6.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_7.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_8.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_9.bm | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../{sfw => }/L1_Read_books_128x64/frame_0.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_1.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_2.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_3.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_4.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_5.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_6.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_7.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_8.bm | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_1.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_10.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_11.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_2.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_3.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_4.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_5.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_6.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_7.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_8.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_9.bm | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.bm | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_1.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_2.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_3.bm | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.bm | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../{sfw => }/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_10.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.bm | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.bm | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.bm | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{sfw => }/manifest.txt | 0 .../Anims}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../PaxGod_TikTok_Marketing/frame_10.bm | Bin .../PaxGod_TikTok_Marketing/frame_11.bm | Bin .../PaxGod_TikTok_Marketing/frame_12.bm | Bin .../PaxGod_TikTok_Marketing/frame_13.bm | Bin .../PaxGod_TikTok_Marketing/frame_14.bm | Bin .../PaxGod_TikTok_Marketing/frame_15.bm | Bin .../PaxGod_TikTok_Marketing/frame_16.bm | Bin .../PaxGod_TikTok_Marketing/frame_17.bm | Bin .../PaxGod_TikTok_Marketing/frame_18.bm | Bin .../PaxGod_TikTok_Marketing/frame_19.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../PaxGod_TikTok_Marketing/frame_20.bm | Bin .../PaxGod_TikTok_Marketing/frame_21.bm | Bin .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 0 -> 472 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 0 -> 462 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 0 -> 487 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 0 -> 488 bytes .../Anims}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../NSFW/Anims}/lvl_1/frame_0.bm | Bin .../NSFW/Anims}/lvl_1/frame_1.bm | Bin .../NSFW/Anims}/lvl_1/frame_10.bm | Bin .../NSFW/Anims}/lvl_1/frame_11.bm | Bin .../NSFW/Anims}/lvl_1/frame_12.bm | Bin .../NSFW/Anims}/lvl_1/frame_13.bm | Bin .../NSFW/Anims}/lvl_1/frame_14.bm | Bin .../NSFW/Anims}/lvl_1/frame_15.bm | Bin .../NSFW/Anims}/lvl_1/frame_16.bm | Bin .../NSFW/Anims}/lvl_1/frame_17.bm | Bin .../NSFW/Anims}/lvl_1/frame_18.bm | Bin .../NSFW/Anims}/lvl_1/frame_19.bm | Bin .../NSFW/Anims}/lvl_1/frame_2.bm | Bin .../NSFW/Anims}/lvl_1/frame_20.bm | Bin .../NSFW/Anims}/lvl_1/frame_21.bm | Bin .../NSFW/Anims}/lvl_1/frame_22.bm | Bin .../NSFW/Anims}/lvl_1/frame_23.bm | Bin .../NSFW/Anims}/lvl_1/frame_24.bm | Bin .../NSFW/Anims}/lvl_1/frame_25.bm | Bin .../NSFW/Anims}/lvl_1/frame_26.bm | Bin .../NSFW/Anims}/lvl_1/frame_27.bm | Bin .../NSFW/Anims}/lvl_1/frame_28.bm | Bin .../NSFW/Anims}/lvl_1/frame_29.bm | Bin .../NSFW/Anims}/lvl_1/frame_3.bm | Bin .../NSFW/Anims}/lvl_1/frame_30.bm | Bin .../NSFW/Anims}/lvl_1/frame_4.bm | Bin .../NSFW/Anims}/lvl_1/frame_5.bm | Bin .../NSFW/Anims}/lvl_1/frame_6.bm | Bin .../NSFW/Anims}/lvl_1/frame_7.bm | Bin .../NSFW/Anims}/lvl_1/frame_8.bm | Bin .../NSFW/Anims}/lvl_1/frame_9.bm | Bin .../NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.bm | Bin .../NSFW/Anims}/lvl_10/frame_1.bm | Bin .../NSFW/Anims}/lvl_10/frame_10.bm | Bin .../NSFW/Anims}/lvl_10/frame_11.bm | Bin .../NSFW/Anims}/lvl_10/frame_12.bm | Bin .../NSFW/Anims}/lvl_10/frame_13.bm | Bin .../NSFW/Anims}/lvl_10/frame_14.bm | Bin .../NSFW/Anims}/lvl_10/frame_15.bm | Bin .../NSFW/Anims}/lvl_10/frame_16.bm | Bin .../NSFW/Anims}/lvl_10/frame_17.bm | Bin .../NSFW/Anims}/lvl_10/frame_18.bm | Bin .../NSFW/Anims}/lvl_10/frame_19.bm | Bin .../NSFW/Anims}/lvl_10/frame_2.bm | Bin .../NSFW/Anims}/lvl_10/frame_20.bm | Bin .../NSFW/Anims}/lvl_10/frame_21.bm | Bin .../NSFW/Anims}/lvl_10/frame_22.bm | Bin .../NSFW/Anims}/lvl_10/frame_23.bm | Bin .../NSFW/Anims}/lvl_10/frame_24.bm | Bin .../NSFW/Anims}/lvl_10/frame_25.bm | Bin .../NSFW/Anims}/lvl_10/frame_26.bm | Bin .../NSFW/Anims}/lvl_10/frame_27.bm | Bin .../NSFW/Anims}/lvl_10/frame_3.bm | Bin .../NSFW/Anims}/lvl_10/frame_4.bm | Bin .../NSFW/Anims}/lvl_10/frame_5.bm | Bin .../NSFW/Anims}/lvl_10/frame_6.bm | Bin .../NSFW/Anims}/lvl_10/frame_7.bm | Bin .../NSFW/Anims}/lvl_10/frame_8.bm | Bin .../NSFW/Anims}/lvl_10/frame_9.bm | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.bm | Bin .../NSFW/Anims}/lvl_11/frame_1.bm | Bin .../NSFW/Anims}/lvl_11/frame_10.bm | Bin .../NSFW/Anims}/lvl_11/frame_11.bm | Bin .../NSFW/Anims}/lvl_11/frame_12.bm | Bin .../NSFW/Anims}/lvl_11/frame_13.bm | Bin .../NSFW/Anims}/lvl_11/frame_14.bm | Bin .../NSFW/Anims}/lvl_11/frame_15.bm | Bin .../NSFW/Anims}/lvl_11/frame_16.bm | Bin .../NSFW/Anims}/lvl_11/frame_17.bm | Bin .../NSFW/Anims}/lvl_11/frame_18.bm | Bin .../NSFW/Anims}/lvl_11/frame_19.bm | Bin .../NSFW/Anims}/lvl_11/frame_2.bm | Bin .../NSFW/Anims}/lvl_11/frame_20.bm | Bin .../NSFW/Anims}/lvl_11/frame_21.bm | Bin .../NSFW/Anims}/lvl_11/frame_22.bm | Bin .../NSFW/Anims}/lvl_11/frame_23.bm | Bin .../NSFW/Anims}/lvl_11/frame_24.bm | Bin .../NSFW/Anims}/lvl_11/frame_25.bm | Bin .../NSFW/Anims}/lvl_11/frame_26.bm | Bin .../NSFW/Anims}/lvl_11/frame_27.bm | Bin .../NSFW/Anims}/lvl_11/frame_28.bm | Bin .../NSFW/Anims}/lvl_11/frame_29.bm | Bin .../NSFW/Anims}/lvl_11/frame_3.bm | Bin .../NSFW/Anims}/lvl_11/frame_30.bm | Bin .../NSFW/Anims}/lvl_11/frame_31.bm | Bin .../NSFW/Anims}/lvl_11/frame_32.bm | Bin .../NSFW/Anims}/lvl_11/frame_33.bm | Bin .../NSFW/Anims}/lvl_11/frame_34.bm | Bin .../NSFW/Anims}/lvl_11/frame_35.bm | Bin .../NSFW/Anims}/lvl_11/frame_36.bm | Bin .../NSFW/Anims}/lvl_11/frame_37.bm | Bin .../NSFW/Anims}/lvl_11/frame_38.bm | Bin .../NSFW/Anims}/lvl_11/frame_39.bm | Bin .../NSFW/Anims}/lvl_11/frame_4.bm | Bin .../NSFW/Anims}/lvl_11/frame_40.bm | Bin .../NSFW/Anims}/lvl_11/frame_41.bm | Bin .../NSFW/Anims}/lvl_11/frame_42.bm | Bin .../NSFW/Anims}/lvl_11/frame_43.bm | Bin .../NSFW/Anims}/lvl_11/frame_44.bm | Bin .../NSFW/Anims}/lvl_11/frame_45.bm | Bin .../NSFW/Anims}/lvl_11/frame_46.bm | Bin .../NSFW/Anims}/lvl_11/frame_47.bm | Bin .../NSFW/Anims}/lvl_11/frame_48.bm | Bin .../NSFW/Anims}/lvl_11/frame_49.bm | Bin .../NSFW/Anims}/lvl_11/frame_5.bm | Bin .../NSFW/Anims}/lvl_11/frame_6.bm | Bin .../NSFW/Anims}/lvl_11/frame_7.bm | Bin .../NSFW/Anims}/lvl_11/frame_8.bm | Bin .../NSFW/Anims}/lvl_11/frame_9.bm | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.bm | Bin .../NSFW/Anims}/lvl_12/frame_1.bm | Bin .../NSFW/Anims}/lvl_12/frame_10.bm | Bin .../NSFW/Anims}/lvl_12/frame_11.bm | Bin .../NSFW/Anims}/lvl_12/frame_12.bm | Bin .../NSFW/Anims}/lvl_12/frame_13.bm | Bin .../NSFW/Anims}/lvl_12/frame_14.bm | Bin .../NSFW/Anims}/lvl_12/frame_15.bm | Bin .../NSFW/Anims}/lvl_12/frame_2.bm | Bin .../NSFW/Anims}/lvl_12/frame_3.bm | Bin .../NSFW/Anims}/lvl_12/frame_4.bm | Bin .../NSFW/Anims}/lvl_12/frame_5.bm | Bin .../NSFW/Anims}/lvl_12/frame_6.bm | Bin .../NSFW/Anims}/lvl_12/frame_7.bm | Bin .../NSFW/Anims}/lvl_12/frame_8.bm | Bin .../NSFW/Anims}/lvl_12/frame_9.bm | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.bm | Bin .../NSFW/Anims}/lvl_13/frame_1.bm | Bin .../NSFW/Anims}/lvl_13/frame_10.bm | Bin .../NSFW/Anims}/lvl_13/frame_2.bm | Bin .../NSFW/Anims}/lvl_13/frame_3.bm | Bin .../NSFW/Anims}/lvl_13/frame_4.bm | Bin .../NSFW/Anims}/lvl_13/frame_5.bm | Bin .../NSFW/Anims}/lvl_13/frame_6.bm | Bin .../NSFW/Anims}/lvl_13/frame_7.bm | Bin .../NSFW/Anims}/lvl_13/frame_8.bm | Bin .../NSFW/Anims}/lvl_13/frame_9.bm | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.bm | Bin .../NSFW/Anims}/lvl_14/frame_1.bm | Bin .../NSFW/Anims}/lvl_14/frame_2.bm | Bin .../NSFW/Anims}/lvl_14/frame_3.bm | Bin .../NSFW/Anims}/lvl_14/frame_4.bm | Bin .../NSFW/Anims}/lvl_14/frame_5.bm | Bin .../NSFW/Anims}/lvl_14/frame_6.bm | Bin .../NSFW/Anims}/lvl_14/frame_7.bm | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.bm | Bin .../NSFW/Anims}/lvl_15/frame_1.bm | Bin .../NSFW/Anims}/lvl_15/frame_10.bm | Bin .../NSFW/Anims}/lvl_15/frame_11.bm | Bin .../NSFW/Anims}/lvl_15/frame_12.bm | Bin .../NSFW/Anims}/lvl_15/frame_13.bm | Bin .../NSFW/Anims}/lvl_15/frame_14.bm | Bin .../NSFW/Anims}/lvl_15/frame_15.bm | Bin .../NSFW/Anims}/lvl_15/frame_16.bm | Bin .../NSFW/Anims}/lvl_15/frame_17.bm | Bin .../NSFW/Anims}/lvl_15/frame_18.bm | Bin .../NSFW/Anims}/lvl_15/frame_19.bm | Bin .../NSFW/Anims}/lvl_15/frame_2.bm | Bin .../NSFW/Anims}/lvl_15/frame_20.bm | Bin .../NSFW/Anims}/lvl_15/frame_21.bm | Bin .../NSFW/Anims}/lvl_15/frame_3.bm | Bin .../NSFW/Anims}/lvl_15/frame_4.bm | Bin .../NSFW/Anims}/lvl_15/frame_5.bm | Bin .../NSFW/Anims}/lvl_15/frame_6.bm | Bin .../NSFW/Anims}/lvl_15/frame_7.bm | Bin .../NSFW/Anims}/lvl_15/frame_8.bm | Bin .../NSFW/Anims}/lvl_15/frame_9.bm | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.bm | Bin .../NSFW/Anims}/lvl_16/frame_1.bm | Bin .../NSFW/Anims}/lvl_16/frame_10.bm | Bin .../NSFW/Anims}/lvl_16/frame_11.bm | Bin .../NSFW/Anims}/lvl_16/frame_12.bm | Bin .../NSFW/Anims}/lvl_16/frame_13.bm | Bin .../NSFW/Anims}/lvl_16/frame_14.bm | Bin .../NSFW/Anims}/lvl_16/frame_15.bm | Bin .../NSFW/Anims}/lvl_16/frame_16.bm | Bin .../NSFW/Anims}/lvl_16/frame_17.bm | Bin .../NSFW/Anims}/lvl_16/frame_18.bm | Bin .../NSFW/Anims}/lvl_16/frame_19.bm | Bin .../NSFW/Anims}/lvl_16/frame_2.bm | Bin .../NSFW/Anims}/lvl_16/frame_20.bm | Bin .../NSFW/Anims}/lvl_16/frame_3.bm | Bin .../NSFW/Anims}/lvl_16/frame_4.bm | Bin .../NSFW/Anims}/lvl_16/frame_5.bm | Bin .../NSFW/Anims}/lvl_16/frame_6.bm | Bin .../NSFW/Anims}/lvl_16/frame_7.bm | Bin .../NSFW/Anims}/lvl_16/frame_8.bm | Bin .../NSFW/Anims}/lvl_16/frame_9.bm | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.bm | Bin .../NSFW/Anims}/lvl_17/frame_1.bm | Bin .../NSFW/Anims}/lvl_17/frame_10.bm | Bin .../NSFW/Anims}/lvl_17/frame_11.bm | Bin .../NSFW/Anims}/lvl_17/frame_12.bm | Bin .../NSFW/Anims}/lvl_17/frame_13.bm | Bin .../NSFW/Anims}/lvl_17/frame_14.bm | Bin .../NSFW/Anims}/lvl_17/frame_15.bm | Bin .../NSFW/Anims}/lvl_17/frame_16.bm | Bin .../NSFW/Anims}/lvl_17/frame_17.bm | Bin .../NSFW/Anims}/lvl_17/frame_18.bm | Bin .../NSFW/Anims}/lvl_17/frame_19.bm | Bin .../NSFW/Anims}/lvl_17/frame_2.bm | Bin .../NSFW/Anims}/lvl_17/frame_20.bm | Bin .../NSFW/Anims}/lvl_17/frame_21.bm | Bin .../NSFW/Anims}/lvl_17/frame_22.bm | Bin .../NSFW/Anims}/lvl_17/frame_23.bm | Bin .../NSFW/Anims}/lvl_17/frame_24.bm | Bin .../NSFW/Anims}/lvl_17/frame_25.bm | Bin .../NSFW/Anims}/lvl_17/frame_26.bm | Bin .../NSFW/Anims}/lvl_17/frame_27.bm | Bin .../NSFW/Anims}/lvl_17/frame_28.bm | Bin .../NSFW/Anims}/lvl_17/frame_29.bm | Bin .../NSFW/Anims}/lvl_17/frame_3.bm | Bin .../NSFW/Anims}/lvl_17/frame_30.bm | Bin .../NSFW/Anims}/lvl_17/frame_31.bm | Bin .../NSFW/Anims}/lvl_17/frame_4.bm | Bin .../NSFW/Anims}/lvl_17/frame_5.bm | Bin .../NSFW/Anims}/lvl_17/frame_6.bm | Bin .../NSFW/Anims}/lvl_17/frame_7.bm | Bin .../NSFW/Anims}/lvl_17/frame_8.bm | Bin .../NSFW/Anims}/lvl_17/frame_9.bm | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.bm | Bin .../NSFW/Anims}/lvl_18/frame_1.bm | Bin .../NSFW/Anims}/lvl_18/frame_10.bm | Bin .../NSFW/Anims}/lvl_18/frame_11.bm | Bin .../NSFW/Anims}/lvl_18/frame_12.bm | Bin .../NSFW/Anims}/lvl_18/frame_13.bm | Bin .../NSFW/Anims}/lvl_18/frame_14.bm | Bin .../NSFW/Anims}/lvl_18/frame_15.bm | Bin .../NSFW/Anims}/lvl_18/frame_16.bm | Bin .../NSFW/Anims}/lvl_18/frame_17.bm | Bin .../NSFW/Anims}/lvl_18/frame_18.bm | Bin .../NSFW/Anims}/lvl_18/frame_19.bm | Bin .../NSFW/Anims}/lvl_18/frame_2.bm | Bin .../NSFW/Anims}/lvl_18/frame_20.bm | Bin .../NSFW/Anims}/lvl_18/frame_21.bm | Bin .../NSFW/Anims}/lvl_18/frame_22.bm | Bin .../NSFW/Anims}/lvl_18/frame_3.bm | Bin .../NSFW/Anims}/lvl_18/frame_4.bm | Bin .../NSFW/Anims}/lvl_18/frame_5.bm | Bin .../NSFW/Anims}/lvl_18/frame_6.bm | Bin .../NSFW/Anims}/lvl_18/frame_7.bm | Bin .../NSFW/Anims}/lvl_18/frame_8.bm | Bin .../NSFW/Anims}/lvl_18/frame_9.bm | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.bm | Bin .../NSFW/Anims}/lvl_19/frame_1.bm | Bin .../NSFW/Anims}/lvl_19/frame_10.bm | Bin .../NSFW/Anims}/lvl_19/frame_11.bm | Bin .../NSFW/Anims}/lvl_19/frame_12.bm | Bin .../NSFW/Anims}/lvl_19/frame_13.bm | Bin .../NSFW/Anims}/lvl_19/frame_14.bm | Bin .../NSFW/Anims}/lvl_19/frame_15.bm | Bin .../NSFW/Anims}/lvl_19/frame_16.bm | Bin .../NSFW/Anims}/lvl_19/frame_17.bm | Bin .../NSFW/Anims}/lvl_19/frame_18.bm | Bin .../NSFW/Anims}/lvl_19/frame_19.bm | Bin .../NSFW/Anims}/lvl_19/frame_2.bm | Bin .../NSFW/Anims}/lvl_19/frame_20.bm | Bin .../NSFW/Anims}/lvl_19/frame_21.bm | Bin .../NSFW/Anims}/lvl_19/frame_3.bm | Bin .../NSFW/Anims}/lvl_19/frame_4.bm | Bin .../NSFW/Anims}/lvl_19/frame_5.bm | Bin .../NSFW/Anims}/lvl_19/frame_6.bm | Bin .../NSFW/Anims}/lvl_19/frame_7.bm | Bin .../NSFW/Anims}/lvl_19/frame_8.bm | Bin .../NSFW/Anims}/lvl_19/frame_9.bm | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.bm | Bin .../NSFW/Anims}/lvl_2/frame_1.bm | Bin .../NSFW/Anims}/lvl_2/frame_10.bm | Bin .../NSFW/Anims}/lvl_2/frame_11.bm | Bin .../NSFW/Anims}/lvl_2/frame_12.bm | Bin .../NSFW/Anims}/lvl_2/frame_13.bm | Bin .../NSFW/Anims}/lvl_2/frame_14.bm | Bin .../NSFW/Anims}/lvl_2/frame_2.bm | Bin .../NSFW/Anims}/lvl_2/frame_3.bm | Bin .../NSFW/Anims}/lvl_2/frame_4.bm | Bin .../NSFW/Anims}/lvl_2/frame_5.bm | Bin .../NSFW/Anims}/lvl_2/frame_6.bm | Bin .../NSFW/Anims}/lvl_2/frame_7.bm | Bin .../NSFW/Anims}/lvl_2/frame_8.bm | Bin .../NSFW/Anims}/lvl_2/frame_9.bm | Bin .../NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.bm | Bin .../NSFW/Anims}/lvl_20/frame_1.bm | Bin .../NSFW/Anims}/lvl_20/frame_10.bm | Bin .../NSFW/Anims}/lvl_20/frame_11.bm | Bin .../NSFW/Anims}/lvl_20/frame_12.bm | Bin .../NSFW/Anims}/lvl_20/frame_13.bm | Bin .../NSFW/Anims}/lvl_20/frame_2.bm | Bin .../NSFW/Anims}/lvl_20/frame_3.bm | Bin .../NSFW/Anims}/lvl_20/frame_4.bm | Bin .../NSFW/Anims}/lvl_20/frame_5.bm | Bin .../NSFW/Anims}/lvl_20/frame_6.bm | Bin .../NSFW/Anims}/lvl_20/frame_7.bm | Bin .../NSFW/Anims}/lvl_20/frame_8.bm | Bin .../NSFW/Anims}/lvl_20/frame_9.bm | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.bm | Bin .../NSFW/Anims}/lvl_21/frame_1.bm | Bin .../NSFW/Anims}/lvl_21/frame_2.bm | Bin .../NSFW/Anims}/lvl_21/frame_3.bm | Bin .../NSFW/Anims}/lvl_21/frame_4.bm | Bin .../NSFW/Anims}/lvl_21/frame_5.bm | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.bm | Bin .../NSFW/Anims}/lvl_22/frame_1.bm | Bin .../NSFW/Anims}/lvl_22/frame_10.bm | Bin .../NSFW/Anims}/lvl_22/frame_11.bm | Bin .../NSFW/Anims}/lvl_22/frame_12.bm | Bin .../NSFW/Anims}/lvl_22/frame_13.bm | Bin .../NSFW/Anims}/lvl_22/frame_14.bm | Bin .../NSFW/Anims}/lvl_22/frame_15.bm | Bin .../NSFW/Anims}/lvl_22/frame_16.bm | Bin .../NSFW/Anims}/lvl_22/frame_17.bm | Bin .../NSFW/Anims}/lvl_22/frame_18.bm | Bin .../NSFW/Anims}/lvl_22/frame_19.bm | Bin .../NSFW/Anims}/lvl_22/frame_2.bm | Bin .../NSFW/Anims}/lvl_22/frame_20.bm | Bin .../NSFW/Anims}/lvl_22/frame_21.bm | Bin .../NSFW/Anims}/lvl_22/frame_22.bm | Bin .../NSFW/Anims}/lvl_22/frame_23.bm | Bin .../NSFW/Anims}/lvl_22/frame_24.bm | Bin .../NSFW/Anims}/lvl_22/frame_25.bm | Bin .../NSFW/Anims}/lvl_22/frame_26.bm | Bin .../NSFW/Anims}/lvl_22/frame_27.bm | Bin .../NSFW/Anims}/lvl_22/frame_28.bm | Bin .../NSFW/Anims}/lvl_22/frame_29.bm | Bin .../NSFW/Anims}/lvl_22/frame_3.bm | Bin .../NSFW/Anims}/lvl_22/frame_30.bm | Bin .../NSFW/Anims}/lvl_22/frame_31.bm | Bin .../NSFW/Anims}/lvl_22/frame_32.bm | Bin .../NSFW/Anims}/lvl_22/frame_33.bm | Bin .../NSFW/Anims}/lvl_22/frame_34.bm | Bin .../NSFW/Anims}/lvl_22/frame_35.bm | Bin .../NSFW/Anims}/lvl_22/frame_36.bm | Bin .../NSFW/Anims}/lvl_22/frame_37.bm | Bin .../NSFW/Anims}/lvl_22/frame_38.bm | Bin .../NSFW/Anims}/lvl_22/frame_39.bm | Bin .../NSFW/Anims}/lvl_22/frame_4.bm | Bin .../NSFW/Anims}/lvl_22/frame_40.bm | Bin .../NSFW/Anims}/lvl_22/frame_41.bm | Bin .../NSFW/Anims}/lvl_22/frame_42.bm | Bin .../NSFW/Anims}/lvl_22/frame_43.bm | Bin .../NSFW/Anims}/lvl_22/frame_44.bm | Bin .../NSFW/Anims}/lvl_22/frame_45.bm | Bin .../NSFW/Anims}/lvl_22/frame_46.bm | Bin .../NSFW/Anims}/lvl_22/frame_47.bm | Bin .../NSFW/Anims}/lvl_22/frame_48.bm | Bin .../NSFW/Anims}/lvl_22/frame_49.bm | Bin .../NSFW/Anims}/lvl_22/frame_5.bm | Bin .../NSFW/Anims}/lvl_22/frame_50.bm | Bin .../NSFW/Anims}/lvl_22/frame_51.bm | Bin .../NSFW/Anims}/lvl_22/frame_52.bm | Bin .../NSFW/Anims}/lvl_22/frame_53.bm | Bin .../NSFW/Anims}/lvl_22/frame_54.bm | Bin .../NSFW/Anims}/lvl_22/frame_55.bm | Bin .../NSFW/Anims}/lvl_22/frame_56.bm | Bin .../NSFW/Anims}/lvl_22/frame_57.bm | Bin .../NSFW/Anims}/lvl_22/frame_58.bm | Bin .../NSFW/Anims}/lvl_22/frame_59.bm | Bin .../NSFW/Anims}/lvl_22/frame_6.bm | Bin .../NSFW/Anims}/lvl_22/frame_7.bm | Bin .../NSFW/Anims}/lvl_22/frame_8.bm | Bin .../NSFW/Anims}/lvl_22/frame_9.bm | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.bm | Bin .../NSFW/Anims}/lvl_23/frame_1.bm | Bin .../NSFW/Anims}/lvl_23/frame_10.bm | Bin .../NSFW/Anims}/lvl_23/frame_11.bm | Bin .../NSFW/Anims}/lvl_23/frame_12.bm | Bin .../NSFW/Anims}/lvl_23/frame_13.bm | Bin .../NSFW/Anims}/lvl_23/frame_14.bm | Bin .../NSFW/Anims}/lvl_23/frame_15.bm | Bin .../NSFW/Anims}/lvl_23/frame_16.bm | Bin .../NSFW/Anims}/lvl_23/frame_2.bm | Bin .../NSFW/Anims}/lvl_23/frame_3.bm | Bin .../NSFW/Anims}/lvl_23/frame_4.bm | Bin .../NSFW/Anims}/lvl_23/frame_5.bm | Bin .../NSFW/Anims}/lvl_23/frame_6.bm | Bin .../NSFW/Anims}/lvl_23/frame_7.bm | Bin .../NSFW/Anims}/lvl_23/frame_8.bm | Bin .../NSFW/Anims}/lvl_23/frame_9.bm | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.bm | Bin .../NSFW/Anims}/lvl_24/frame_1.bm | Bin .../NSFW/Anims}/lvl_24/frame_10.bm | Bin .../NSFW/Anims}/lvl_24/frame_11.bm | Bin .../NSFW/Anims}/lvl_24/frame_12.bm | Bin .../NSFW/Anims}/lvl_24/frame_13.bm | Bin .../NSFW/Anims}/lvl_24/frame_14.bm | Bin .../NSFW/Anims}/lvl_24/frame_15.bm | Bin .../NSFW/Anims}/lvl_24/frame_16.bm | Bin .../NSFW/Anims}/lvl_24/frame_17.bm | Bin .../NSFW/Anims}/lvl_24/frame_18.bm | Bin .../NSFW/Anims}/lvl_24/frame_19.bm | Bin .../NSFW/Anims}/lvl_24/frame_2.bm | Bin .../NSFW/Anims}/lvl_24/frame_20.bm | Bin .../NSFW/Anims}/lvl_24/frame_21.bm | Bin .../NSFW/Anims}/lvl_24/frame_22.bm | Bin .../NSFW/Anims}/lvl_24/frame_23.bm | Bin .../NSFW/Anims}/lvl_24/frame_24.bm | Bin .../NSFW/Anims}/lvl_24/frame_25.bm | Bin .../NSFW/Anims}/lvl_24/frame_26.bm | Bin .../NSFW/Anims}/lvl_24/frame_27.bm | Bin .../NSFW/Anims}/lvl_24/frame_28.bm | Bin .../NSFW/Anims}/lvl_24/frame_29.bm | Bin .../NSFW/Anims}/lvl_24/frame_3.bm | Bin .../NSFW/Anims}/lvl_24/frame_4.bm | Bin .../NSFW/Anims}/lvl_24/frame_5.bm | Bin .../NSFW/Anims}/lvl_24/frame_6.bm | Bin .../NSFW/Anims}/lvl_24/frame_7.bm | Bin .../NSFW/Anims}/lvl_24/frame_8.bm | Bin .../NSFW/Anims}/lvl_24/frame_9.bm | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.bm | Bin .../NSFW/Anims}/lvl_25/frame_1.bm | Bin .../NSFW/Anims}/lvl_25/frame_10.bm | Bin .../NSFW/Anims}/lvl_25/frame_11.bm | Bin .../NSFW/Anims}/lvl_25/frame_12.bm | Bin .../NSFW/Anims}/lvl_25/frame_13.bm | Bin .../NSFW/Anims}/lvl_25/frame_14.bm | Bin .../NSFW/Anims}/lvl_25/frame_15.bm | Bin .../NSFW/Anims}/lvl_25/frame_16.bm | Bin .../NSFW/Anims}/lvl_25/frame_17.bm | Bin .../NSFW/Anims}/lvl_25/frame_18.bm | Bin .../NSFW/Anims}/lvl_25/frame_19.bm | Bin .../NSFW/Anims}/lvl_25/frame_2.bm | Bin .../NSFW/Anims}/lvl_25/frame_20.bm | Bin .../NSFW/Anims}/lvl_25/frame_21.bm | Bin .../NSFW/Anims}/lvl_25/frame_22.bm | Bin .../NSFW/Anims}/lvl_25/frame_23.bm | Bin .../NSFW/Anims}/lvl_25/frame_24.bm | Bin .../NSFW/Anims}/lvl_25/frame_25.bm | Bin .../NSFW/Anims}/lvl_25/frame_26.bm | Bin .../NSFW/Anims}/lvl_25/frame_27.bm | Bin .../NSFW/Anims}/lvl_25/frame_28.bm | Bin .../NSFW/Anims}/lvl_25/frame_29.bm | Bin .../NSFW/Anims}/lvl_25/frame_3.bm | Bin .../NSFW/Anims}/lvl_25/frame_30.bm | Bin .../NSFW/Anims}/lvl_25/frame_31.bm | Bin .../NSFW/Anims}/lvl_25/frame_32.bm | Bin .../NSFW/Anims}/lvl_25/frame_33.bm | Bin .../NSFW/Anims}/lvl_25/frame_34.bm | Bin .../NSFW/Anims}/lvl_25/frame_35.bm | Bin .../NSFW/Anims}/lvl_25/frame_4.bm | Bin .../NSFW/Anims}/lvl_25/frame_5.bm | Bin .../NSFW/Anims}/lvl_25/frame_6.bm | Bin .../NSFW/Anims}/lvl_25/frame_7.bm | Bin .../NSFW/Anims}/lvl_25/frame_8.bm | Bin .../NSFW/Anims}/lvl_25/frame_9.bm | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.bm | Bin .../NSFW/Anims}/lvl_26/frame_1.bm | Bin .../NSFW/Anims}/lvl_26/frame_10.bm | Bin .../NSFW/Anims}/lvl_26/frame_11.bm | Bin .../NSFW/Anims}/lvl_26/frame_2.bm | Bin .../NSFW/Anims}/lvl_26/frame_3.bm | Bin .../NSFW/Anims}/lvl_26/frame_4.bm | Bin .../NSFW/Anims}/lvl_26/frame_5.bm | Bin .../NSFW/Anims}/lvl_26/frame_6.bm | Bin .../NSFW/Anims}/lvl_26/frame_7.bm | Bin .../NSFW/Anims}/lvl_26/frame_8.bm | Bin .../NSFW/Anims}/lvl_26/frame_9.bm | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.bm | Bin .../NSFW/Anims}/lvl_27/frame_1.bm | Bin .../NSFW/Anims}/lvl_27/frame_10.bm | Bin .../NSFW/Anims}/lvl_27/frame_11.bm | Bin .../NSFW/Anims}/lvl_27/frame_12.bm | Bin .../NSFW/Anims}/lvl_27/frame_13.bm | Bin .../NSFW/Anims}/lvl_27/frame_14.bm | Bin .../NSFW/Anims}/lvl_27/frame_15.bm | Bin .../NSFW/Anims}/lvl_27/frame_16.bm | Bin .../NSFW/Anims}/lvl_27/frame_17.bm | Bin .../NSFW/Anims}/lvl_27/frame_18.bm | Bin .../NSFW/Anims}/lvl_27/frame_19.bm | Bin .../NSFW/Anims}/lvl_27/frame_2.bm | Bin .../NSFW/Anims}/lvl_27/frame_20.bm | Bin .../NSFW/Anims}/lvl_27/frame_21.bm | Bin .../NSFW/Anims}/lvl_27/frame_3.bm | Bin .../NSFW/Anims}/lvl_27/frame_4.bm | Bin .../NSFW/Anims}/lvl_27/frame_5.bm | Bin .../NSFW/Anims}/lvl_27/frame_6.bm | Bin .../NSFW/Anims}/lvl_27/frame_7.bm | Bin .../NSFW/Anims}/lvl_27/frame_8.bm | Bin .../NSFW/Anims}/lvl_27/frame_9.bm | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.bm | Bin .../NSFW/Anims}/lvl_28/frame_1.bm | Bin .../NSFW/Anims}/lvl_28/frame_2.bm | Bin .../NSFW/Anims}/lvl_28/frame_3.bm | Bin .../NSFW/Anims}/lvl_28/frame_4.bm | Bin .../NSFW/Anims}/lvl_28/frame_5.bm | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.bm | Bin .../NSFW/Anims}/lvl_29/frame_1.bm | Bin .../NSFW/Anims}/lvl_29/frame_10.bm | Bin .../NSFW/Anims}/lvl_29/frame_11.bm | Bin .../NSFW/Anims}/lvl_29/frame_12.bm | Bin .../NSFW/Anims}/lvl_29/frame_13.bm | Bin .../NSFW/Anims}/lvl_29/frame_14.bm | Bin .../NSFW/Anims}/lvl_29/frame_15.bm | Bin .../NSFW/Anims}/lvl_29/frame_16.bm | Bin .../NSFW/Anims}/lvl_29/frame_17.bm | Bin .../NSFW/Anims}/lvl_29/frame_18.bm | Bin .../NSFW/Anims}/lvl_29/frame_19.bm | Bin .../NSFW/Anims}/lvl_29/frame_2.bm | Bin .../NSFW/Anims}/lvl_29/frame_20.bm | Bin .../NSFW/Anims}/lvl_29/frame_21.bm | Bin .../NSFW/Anims}/lvl_29/frame_22.bm | Bin .../NSFW/Anims}/lvl_29/frame_23.bm | Bin .../NSFW/Anims}/lvl_29/frame_24.bm | Bin .../NSFW/Anims}/lvl_29/frame_25.bm | Bin .../NSFW/Anims}/lvl_29/frame_26.bm | Bin .../NSFW/Anims}/lvl_29/frame_27.bm | Bin .../NSFW/Anims}/lvl_29/frame_28.bm | Bin .../NSFW/Anims}/lvl_29/frame_29.bm | Bin .../NSFW/Anims}/lvl_29/frame_3.bm | Bin .../NSFW/Anims}/lvl_29/frame_30.bm | Bin .../NSFW/Anims}/lvl_29/frame_31.bm | Bin .../NSFW/Anims}/lvl_29/frame_32.bm | Bin .../NSFW/Anims}/lvl_29/frame_33.bm | Bin .../NSFW/Anims}/lvl_29/frame_34.bm | Bin .../NSFW/Anims}/lvl_29/frame_35.bm | Bin .../NSFW/Anims}/lvl_29/frame_36.bm | Bin .../NSFW/Anims}/lvl_29/frame_37.bm | Bin .../NSFW/Anims}/lvl_29/frame_38.bm | Bin .../NSFW/Anims}/lvl_29/frame_39.bm | Bin .../NSFW/Anims}/lvl_29/frame_4.bm | Bin .../NSFW/Anims}/lvl_29/frame_40.bm | Bin .../NSFW/Anims}/lvl_29/frame_41.bm | Bin .../NSFW/Anims}/lvl_29/frame_42.bm | Bin .../NSFW/Anims}/lvl_29/frame_43.bm | Bin .../NSFW/Anims}/lvl_29/frame_44.bm | Bin .../NSFW/Anims}/lvl_29/frame_45.bm | Bin .../NSFW/Anims}/lvl_29/frame_46.bm | Bin .../NSFW/Anims}/lvl_29/frame_47.bm | Bin .../NSFW/Anims}/lvl_29/frame_48.bm | Bin .../NSFW/Anims}/lvl_29/frame_49.bm | Bin .../NSFW/Anims}/lvl_29/frame_5.bm | Bin .../NSFW/Anims}/lvl_29/frame_50.bm | Bin .../NSFW/Anims}/lvl_29/frame_51.bm | Bin .../NSFW/Anims}/lvl_29/frame_6.bm | Bin .../NSFW/Anims}/lvl_29/frame_7.bm | Bin .../NSFW/Anims}/lvl_29/frame_8.bm | Bin .../NSFW/Anims}/lvl_29/frame_9.bm | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.bm | Bin .../NSFW/Anims}/lvl_3/frame_1.bm | Bin .../NSFW/Anims}/lvl_3/frame_10.bm | Bin .../NSFW/Anims}/lvl_3/frame_11.bm | Bin .../NSFW/Anims}/lvl_3/frame_12.bm | Bin .../NSFW/Anims}/lvl_3/frame_13.bm | Bin .../NSFW/Anims}/lvl_3/frame_14.bm | Bin .../NSFW/Anims}/lvl_3/frame_2.bm | Bin .../NSFW/Anims}/lvl_3/frame_3.bm | Bin .../NSFW/Anims}/lvl_3/frame_4.bm | Bin .../NSFW/Anims}/lvl_3/frame_5.bm | Bin .../NSFW/Anims}/lvl_3/frame_6.bm | Bin .../NSFW/Anims}/lvl_3/frame_7.bm | Bin .../NSFW/Anims}/lvl_3/frame_8.bm | Bin .../NSFW/Anims}/lvl_3/frame_9.bm | Bin .../NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.bm | Bin .../NSFW/Anims}/lvl_30/frame_1.bm | Bin .../NSFW/Anims}/lvl_30/frame_10.bm | Bin .../NSFW/Anims}/lvl_30/frame_11.bm | Bin .../NSFW/Anims}/lvl_30/frame_12.bm | Bin .../NSFW/Anims}/lvl_30/frame_13.bm | Bin .../NSFW/Anims}/lvl_30/frame_14.bm | Bin .../NSFW/Anims}/lvl_30/frame_15.bm | Bin .../NSFW/Anims}/lvl_30/frame_16.bm | Bin .../NSFW/Anims}/lvl_30/frame_17.bm | Bin .../NSFW/Anims}/lvl_30/frame_18.bm | Bin .../NSFW/Anims}/lvl_30/frame_19.bm | Bin .../NSFW/Anims}/lvl_30/frame_2.bm | Bin .../NSFW/Anims}/lvl_30/frame_20.bm | Bin .../NSFW/Anims}/lvl_30/frame_21.bm | Bin .../NSFW/Anims}/lvl_30/frame_22.bm | Bin .../NSFW/Anims}/lvl_30/frame_23.bm | Bin .../NSFW/Anims}/lvl_30/frame_24.bm | Bin .../NSFW/Anims}/lvl_30/frame_25.bm | Bin .../NSFW/Anims}/lvl_30/frame_26.bm | Bin .../NSFW/Anims}/lvl_30/frame_27.bm | Bin .../NSFW/Anims}/lvl_30/frame_28.bm | Bin .../NSFW/Anims}/lvl_30/frame_29.bm | Bin .../NSFW/Anims}/lvl_30/frame_3.bm | Bin .../NSFW/Anims}/lvl_30/frame_30.bm | Bin .../NSFW/Anims}/lvl_30/frame_31.bm | Bin .../NSFW/Anims}/lvl_30/frame_32.bm | Bin .../NSFW/Anims}/lvl_30/frame_33.bm | Bin .../NSFW/Anims}/lvl_30/frame_34.bm | Bin .../NSFW/Anims}/lvl_30/frame_35.bm | Bin .../NSFW/Anims}/lvl_30/frame_36.bm | Bin .../NSFW/Anims}/lvl_30/frame_37.bm | Bin .../NSFW/Anims}/lvl_30/frame_38.bm | Bin .../NSFW/Anims}/lvl_30/frame_39.bm | Bin .../NSFW/Anims}/lvl_30/frame_4.bm | Bin .../NSFW/Anims}/lvl_30/frame_40.bm | Bin .../NSFW/Anims}/lvl_30/frame_41.bm | Bin .../NSFW/Anims}/lvl_30/frame_42.bm | Bin .../NSFW/Anims}/lvl_30/frame_43.bm | Bin .../NSFW/Anims}/lvl_30/frame_44.bm | Bin .../NSFW/Anims}/lvl_30/frame_45.bm | Bin .../NSFW/Anims}/lvl_30/frame_46.bm | Bin .../NSFW/Anims}/lvl_30/frame_47.bm | Bin .../NSFW/Anims}/lvl_30/frame_48.bm | Bin .../NSFW/Anims}/lvl_30/frame_49.bm | Bin .../NSFW/Anims}/lvl_30/frame_5.bm | Bin .../NSFW/Anims}/lvl_30/frame_6.bm | Bin .../NSFW/Anims}/lvl_30/frame_7.bm | Bin .../NSFW/Anims}/lvl_30/frame_8.bm | Bin .../NSFW/Anims}/lvl_30/frame_9.bm | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.bm | Bin .../NSFW/Anims}/lvl_4/frame_1.bm | Bin .../NSFW/Anims}/lvl_4/frame_10.bm | Bin .../NSFW/Anims}/lvl_4/frame_11.bm | Bin .../NSFW/Anims}/lvl_4/frame_12.bm | Bin .../NSFW/Anims}/lvl_4/frame_13.bm | Bin .../NSFW/Anims}/lvl_4/frame_14.bm | Bin .../NSFW/Anims}/lvl_4/frame_15.bm | Bin .../NSFW/Anims}/lvl_4/frame_16.bm | Bin .../NSFW/Anims}/lvl_4/frame_17.bm | Bin .../NSFW/Anims}/lvl_4/frame_18.bm | Bin .../NSFW/Anims}/lvl_4/frame_19.bm | Bin .../NSFW/Anims}/lvl_4/frame_2.bm | Bin .../NSFW/Anims}/lvl_4/frame_3.bm | Bin .../NSFW/Anims}/lvl_4/frame_4.bm | Bin .../NSFW/Anims}/lvl_4/frame_5.bm | Bin .../NSFW/Anims}/lvl_4/frame_6.bm | Bin .../NSFW/Anims}/lvl_4/frame_7.bm | Bin .../NSFW/Anims}/lvl_4/frame_8.bm | Bin .../NSFW/Anims}/lvl_4/frame_9.bm | Bin .../NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.bm | Bin .../NSFW/Anims}/lvl_5/frame_1.bm | Bin .../NSFW/Anims}/lvl_5/frame_10.bm | Bin .../NSFW/Anims}/lvl_5/frame_11.bm | Bin .../NSFW/Anims}/lvl_5/frame_12.bm | Bin .../NSFW/Anims}/lvl_5/frame_13.bm | Bin .../NSFW/Anims}/lvl_5/frame_14.bm | Bin .../NSFW/Anims}/lvl_5/frame_15.bm | Bin .../NSFW/Anims}/lvl_5/frame_16.bm | Bin .../NSFW/Anims}/lvl_5/frame_17.bm | Bin .../NSFW/Anims}/lvl_5/frame_18.bm | Bin .../NSFW/Anims}/lvl_5/frame_19.bm | Bin .../NSFW/Anims}/lvl_5/frame_2.bm | Bin .../NSFW/Anims}/lvl_5/frame_20.bm | Bin .../NSFW/Anims}/lvl_5/frame_21.bm | Bin .../NSFW/Anims}/lvl_5/frame_22.bm | Bin .../NSFW/Anims}/lvl_5/frame_23.bm | Bin .../NSFW/Anims}/lvl_5/frame_24.bm | Bin .../NSFW/Anims}/lvl_5/frame_25.bm | Bin .../NSFW/Anims}/lvl_5/frame_26.bm | Bin .../NSFW/Anims}/lvl_5/frame_27.bm | Bin .../NSFW/Anims}/lvl_5/frame_3.bm | Bin .../NSFW/Anims}/lvl_5/frame_4.bm | Bin .../NSFW/Anims}/lvl_5/frame_5.bm | Bin .../NSFW/Anims}/lvl_5/frame_6.bm | Bin .../NSFW/Anims}/lvl_5/frame_7.bm | Bin .../NSFW/Anims}/lvl_5/frame_8.bm | Bin .../NSFW/Anims}/lvl_5/frame_9.bm | Bin .../NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.bm | Bin .../NSFW/Anims}/lvl_6/frame_1.bm | Bin .../NSFW/Anims}/lvl_6/frame_2.bm | Bin .../NSFW/Anims}/lvl_6/frame_3.bm | Bin .../NSFW/Anims}/lvl_6/frame_4.bm | Bin .../NSFW/Anims}/lvl_6/frame_5.bm | Bin .../NSFW/Anims}/lvl_6/frame_6.bm | Bin .../NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.bm | Bin .../NSFW/Anims}/lvl_7/frame_1.bm | Bin .../NSFW/Anims}/lvl_7/frame_10.bm | Bin .../NSFW/Anims}/lvl_7/frame_11.bm | Bin .../NSFW/Anims}/lvl_7/frame_12.bm | Bin .../NSFW/Anims}/lvl_7/frame_13.bm | Bin .../NSFW/Anims}/lvl_7/frame_2.bm | Bin .../NSFW/Anims}/lvl_7/frame_3.bm | Bin .../NSFW/Anims}/lvl_7/frame_4.bm | Bin .../NSFW/Anims}/lvl_7/frame_5.bm | Bin .../NSFW/Anims}/lvl_7/frame_6.bm | Bin .../NSFW/Anims}/lvl_7/frame_7.bm | Bin .../NSFW/Anims}/lvl_7/frame_8.bm | Bin .../NSFW/Anims}/lvl_7/frame_9.bm | Bin .../NSFW/Anims}/lvl_7/meta.txt | 2 +- .../NSFW/Anims}/lvl_8/frame_0.bm | Bin .../NSFW/Anims}/lvl_8/frame_1.bm | Bin .../NSFW/Anims}/lvl_8/frame_2.bm | Bin .../NSFW/Anims}/lvl_8/frame_3.bm | Bin .../NSFW/Anims}/lvl_8/frame_4.bm | Bin .../NSFW/Anims}/lvl_8/frame_5.bm | Bin .../NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.bm | Bin .../NSFW/Anims}/lvl_9/frame_1.bm | Bin .../NSFW/Anims}/lvl_9/frame_2.bm | Bin .../NSFW/Anims}/lvl_9/frame_3.bm | Bin .../NSFW/Anims}/lvl_9/frame_4.bm | Bin .../NSFW/Anims}/lvl_9/frame_5.bm | Bin .../NSFW/Anims}/lvl_9/frame_6.bm | Bin .../NSFW/Anims}/lvl_9/frame_7.bm | Bin .../NSFW/Anims}/lvl_9/meta.txt | 0 .../NSFW/Anims}/manifest.txt | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 0 -> 480 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 0 -> 325 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 0 -> 513 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 0 -> 375 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 0 -> 286 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 0 -> 297 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 0 -> 525 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 0 -> 525 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 0 -> 502 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 0 -> 352 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 0 -> 458 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 0 -> 257 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 0 -> 684 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 0 -> 609 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 0 -> 481 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 0 -> 482 bytes scripts/assets.py | 35 +-------- 2238 files changed, 50 insertions(+), 125 deletions(-) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_52.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_53.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_54.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_55.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_56.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_57.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_58.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_59.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png create mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png rename assets/{icons/Passport/flipper.png => dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png} (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{sfw => }/manifest.txt (100%) delete mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png delete mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png delete mode 100644 assets/icons/Passport/passport_DB_sfw.png delete mode 100644 assets/icons/Passport/passport_bad2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png rename assets/icons/Passport/{passport_bad1_46x49_sfw.png => passport_bad_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_happy2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png rename assets/icons/Passport/{passport_happy1_46x49_sfw.png => passport_happy_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_okay2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png rename assets/icons/Passport/{passport_okay1_46x49_sfw.png => passport_okay_46x49.png} (100%) delete mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png delete mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png delete mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png delete mode 100644 assets/icons/U2F/Auth_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connected_62x31_sfw.png delete mode 100644 assets/icons/U2F/Error_62x31_sfw.png delete mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png delete mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png delete mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/manifest.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.bm (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index bf7addba9..025246faa 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,9 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* +!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 2c707bbf1..d32eee382 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index ad889cd1c..51bed3835 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define MAX_NAME_LEN 64 @@ -28,7 +28,6 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -49,7 +48,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -68,7 +67,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -78,7 +77,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 162faf2f1..35a6ce1d9 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index eecd32702..fc1c5c4fa 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 5239d72d5..14e06cdf2 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define TAG "AnimationManager" @@ -580,7 +580,7 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); } else { if(stats.level <= 20) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a80a958a3..4263dc0a4 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom Manifest selected"); + FURI_LOG_I(TAG, "Custom manifest selected"); } else { use_asset_pack = false; } @@ -48,14 +48,8 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); - if(xtreme_settings->nsfw_mode) { - furi_string_cat_str(anim_dir, "/nsfw"); - FURI_LOG_I(TAG, "NSFW Manifest selected"); - } else { - furi_string_cat_str(anim_dir, "/sfw"); - FURI_LOG_I(TAG, "SFW Manifest selected"); - } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); + FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index e4f5e788f..c2149253c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index f0430de5d..450c5af23 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index ecab8c333..6fd26138b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 3323cf1df..ba5e65ca0 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,15 +3,6 @@ #include #include -static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); - XTREME_SETTINGS()->nsfw_mode = value; - app->settings_changed = true; - app->assets_changed = true; -} - static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -172,11 +163,6 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); - item = variable_item_list_add( - var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); - variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); - item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 0f6ab998d..b40f272e4 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -17,54 +17,30 @@ void XTREME_ASSETS_LOAD() { xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - if(xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = - &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; - } + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; if(xtreme_settings->asset_pack[0] == '\0') return; + xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; FileInfo info; FuriString* path = furi_string_alloc(); const char* pack = xtreme_settings->asset_pack; diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index c49f5b590..6904a36ab 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,6 +7,7 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { + bool is_nsfw; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index ee0ec5583..13bb574ad 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,7 +18,6 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; - bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_1/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_10/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_11/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_12/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_13/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_14/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_15/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_16/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_17/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_18/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_19/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_2/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_20/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_21/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_52.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_52.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_53.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_53.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_54.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_54.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_55.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_55.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_56.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_56.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_57.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_57.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_58.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_58.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_59.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_59.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_22/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_23/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_24/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_25/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_26/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_27/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_28/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_29/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_3/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_30/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_4/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_5/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_6/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_7/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_8/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_9/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/dolphin/external/nsfw/manifest.txt b/assets/dolphin/custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/dolphin/external/nsfw/manifest.txt rename to assets/dolphin/custom/NSFW/Anims/manifest.txt diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f60598005d41ff05a9f763f42f1a6b7900150e33 GIT binary patch literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e80fea5bd7f694549da1b45e9f3db056342a95cc GIT binary patch literal 3376 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f GIT binary patch literal 4224 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png new file mode 100644 index 0000000000000000000000000000000000000000..98d7e15f9c105cc36c1f6d3473fd915a9687968b GIT binary patch literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..2528ebc95d79335975511a384c70c010d476a8c0 GIT binary patch literal 4862 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..fef503263fd962534e7e5543954612bf6c1faefd GIT binary patch literal 4882 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png new file mode 100644 index 0000000000000000000000000000000000000000..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png new file mode 100644 index 0000000000000000000000000000000000000000..d1164c59483a2242769327b5b2c9ac01acb83186 GIT binary patch literal 3798 zcmV;{4k_`8P)pTh)G02RCwC8*$2#?;~EF>_GxvQwYS<-QF~XTwM!|XY6giFkr+Wl zjKm6}A~Bl^lE#*Fpc18m)E29y_EsFUTCEP}_rCw{yFGcouXU1h&Ykq>^WM)r{?~Q= zuW{dBHGls6Uw--J#~**(WRpz}KKS6%Pd|Oss8Kz8_UzK7%Z@wlxaz8_Zn@=_g9Z)S zV~;($b?c_TYuBzmb#1n)s&tm0|Jb^B@4mtcE10s}a?6=Da^%PjHrQZ|HP+Z`uf6=- zZ@>LE+;GEVk3IJB#~=UeufP8M^Ur^^w`%d?#VfA3;>3v)zxd*d)mB^0#@~GN&4L9B zbi(xe@4s8xy1)JQn~{qaEiykqHSapR{Kw7UxpU_(S+c}Bem?l%gI+BD(n~M-Ic3U} zFTebfzCQTi1BxSwk(pRE{#DhZk3PEk>Z=0;-)EnFrjv+s=FBlvkF|dO`R6YGhXxJY z^`5W4{`%Kne|`V`_wzFvX_$=BUwiGf_19nD?^&~E{qVyNUJU*C8H2few!Kl_U-G_$}6v2G3$pPeh6HjK7D+iF=K{l zk#a$^>HPNFZ=-ym|0h@rxjk(+d-m-5SL~uudST?SzhQC6f`F}k1}O7M?1{zH*{MD< zK#LExw5r)cYr634RKkwTX1bP58z|p&lr`Q?|fA-ryBg~dy2n{K)(9xuQA^2jYL z^ytxJ?X}n5XrqnRTyxE1jyY!Fz=14l0L0Nti&BmE?A^O}Iw4_6o>6AuGGdDn6Z@u+4+iU~V&O7hC*=CywYFll!72K9R^w2|x3>lIr=Gg)? z=JLd@(j)qO_0?BY<`b-q3EzMJJ?v~v)?(<666^1{K^kSS1}(0)-g;C*k(Q!AMi3EK zS!ETmP~3$>yi73esLr~@_=$j6+@P`+lt^RU#NLjIY~g#yCft%ZfefE`B^_`(JsWSl zald~3IK%-59AKocm(_y>6E5NXPV*9^pM=_iDDv2Vem$C|f4y#a9tNl<2hi1RRv` z$y-qdrs~I9({&CWJeXPaAO_PHpxIaEGsVlk`|jJ4M=HYU!T=yz7}L@at^|}$O=e?D z!(lZc7BWub6J7!;{_&(F+jZAnmtHA}^|_s|q6Eha;A$EB{%=6H1?3 z%w5DyzX4WfGS7~>gn)0-89-imdddiG*Ijr0NHx}0OxE;>|w)(fs#zN-FDl7E(ya; zH{L-^Jf{du@G@e=2r<>fAg&XvksE9PuQ}kRrYedo7Kr({veW#Qsz~v3P0>&ba42P z!ZAf&uzDJ1LL(D2-#g|*j>xIei@+ke1!a*ZpM3JEW&{2D>#w)qZoBO!AOOlZED5?? zFmK*G?@Sc>K_5~fE=ZN3Azwl$-taYaW;ojb>S>y%`F^sX_m<2a2d50%Oq(`sr=519 zi5FgYK^z@7Zro*;U3S9_H}HCu*VL&~FTeb9F+5f5r=NcM#v5-ydHCUn%T@MGeZ&F8 zufJ8WxV@#wAP_Wtn=LwKyV0XZQvgM>h55K&he&@J3Fy1;zKeo4-gu*3k2>n8^Upv3 znrp7P>#n;57n$?sn{Vbo)O^7O7udyiRuk|%gnmtK15amO78nSIfWJa^xH_oPXalsoKOc$`v0JoDm2eLk~S< zBWn&FI@BVdTyn`J0ua!*-g>K|OxU{f&O7xd$?dbxK6GiXw)!E&PvXfbr?^gH?Ad3Z zeZ>`5AU^hxUOXd%epWU8GD&adKOZeDqJRQXfG}v29Cahg&%0 zf9fDFM;>`3Hlyje=bp=a^~4svcmZ9F2Os+|u642|^pNC4p*2nvVC8)1L;N&36BH&8 zDR`gb2tDn+_ug2~SgFtqW?389=8;Dp5gcH3ewAsCIN}KVp_iaT3-;@@a#aNB#9+c( zhe7PifgHOjg|fOeTf@g$CvHV~{MTk7oV{Op<&|x>-S&hNPCzxTdh*F9D{TnF;?xWQ z$0QZg!+pmccjRED-=WktT<5K~-fB4?*BkB%Ok(P_*Ip}lSqfZ2lB#}cr~rz6^I`)- zlMY;*|+7GhO6YW4Bs$3O7E17pXIeeAKvZomC@JP`+$cH)U@ z%)$rT5nIh+Q$W0T1mWTt%RU?%W}_5o@h9u6B}AoC)V5jNkMMF~v)PpqJsUZ`YQ%i?!9DlfgXWwO zFYzXUH>o_U3mmMUpXFGRn6ivk4mz@Bp3dA*!UG8vaJ$~kR;5TT1v79lx}alY`hbZR z_uqeiG{67;`zKGHEC{DEFh2Q)H7tVD^>c!LzPPjlU8*Owrgt@w=R5Db!;I7c16`z; zY(ohX+mSwsOosUU^Urn0=z0*va@OKZVmnr_BTvd^Q3*3~@Omh=W|DQMD50kdq*pIT zL9f30s=0bP3f4$76eF;ugo`P;i@j;FaTczsR1&;c!WC}tlCPw)668|M^0QpQ6<9@p zr8V+Xf2hgT#G@RK=#r**&%*XrrJS)x;bpAv;F2Ss0VxE-2TAX&v(94o6mKF!vMsIW zKCB*PS}uTETn7bVijXsB&XjxO43=U^&pGX_su#(W?{f&D^7G84D4~O@=!h& zl_Xo{>{hpF^i)TdAW*-FFh%FR_uh-dQbWvLr%mHRi5LC`ykP3c$XuXh@sJ^Co`3%NoWy%JTkH_`YRixap8MIH{8qe8qpcUmb%D3335|N0GO(mk!H(1;$>NIVL1pK!}I zv2||J3Q(}5Y$oE)L0}`czV5o~;(5^yf2gO1z^!ZvXs$jIiG(4@Ovs5b1!ak0EGvLp ziy>?oH7m5#b?Ta2l$lko`0E@^YE}Lv8gS|hFTBupifhCpGhIlM#z1Uw#+lu0RSp?~ zwuQ1T1i44Z8JwP|p~#1kY)zN^%vz~;d_C>7)5s&;!^#3VMdjF@5?h0V7l%0t`>U?H z3Ztk7*KGu-Db`O#k-99caRWmWI1wutZ4jXop@hjy!<(W@Qsz<_%G`qH#G?#V94SK; zqbHttBEsQfivQ>`2P{7kR7rpyR)bPjBuOyD8S0MMu()1AY`^{X^~($~mK7|K^9J3p zQBp#}gwW$F9pnO6KcEg0!qbQmBX-zf2WL(vyD?+NTzTb{3M;8UZV^R}8a0Z=%wkj8 z#i-=J=t67(I;mUOZ}@moVLXG-79y(^jlcJHL#ertc3y!}-iO0E zWZbxMx7~Id@;T!=13B-qvNNtol-NSM0|yRFm_r_Lt?4A4D}wIrt*YNzfl@B$4OKue zJtYyEI$0#cxQKNVvtmT}R0;XY=);B$qh=-dph1IZD=ebUDO096^*Uo1mh*m&K6c@e zrh`6UzyPFgz51)?v>eKUlG^DK1#@Z0y?n~w8Ej11dUCmCv)t^Ynuc3d03xYEB+F(^ z-ji#~a`6{Cm=ZUz5h0l_x4`q-Ta{8bc8^1qzh=^zOlY!g;_OTrDM3`l<>EPi|4JYO zA_LH4=+L3LeSGn{Cy;4a)#wk#TK+n zuGdQPdwj!kOOrpmiGN52UnUi=Dir~gn^;vk zls?7HQ#aJhZ_iZn33KF0F&c<02$rRA7p+CB3`=Pjf`X3pDKugGH#)l2YM(OSO8@`> M07*qoM6N<$g8AuxBLDyZ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..a48c5330e85c2135866fe99bcb6f3534d06c6d53 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1010ca9da500055615c00359ea7536df4caa01 GIT binary patch literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1 GIT binary patch literal 5122 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 0000000000000000000000000000000000000000..90b589ff8e9f18b28d7fd75ca525a1db24381b06 GIT binary patch literal 4719 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt b/assets/dolphin/external/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt b/assets/dolphin/external/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt b/assets/dolphin/external/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/sfw/manifest.txt b/assets/dolphin/external/manifest.txt similarity index 100% rename from assets/dolphin/external/sfw/manifest.txt rename to assets/dolphin/external/manifest.txt diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index f60598005d41ff05a9f763f42f1a6b7900150e33..34068c300386e0c88e45b40a8995b0a4360ed79f 100644 GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG delta 2594 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 Ef}^?D!2kdN diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png deleted file mode 100644 index 34068c300386e0c88e45b40a8995b0a4360ed79f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index e80fea5bd7f694549da1b45e9f3db056342a95cc..089aaed83507431993a76ca25d32fdd9664c1c84 100644 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj delta 3373 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 D!U>8P diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 93a7ad79cd95cdfe7789366edd8fadb3355e467c..46f559f65f11194c94c15bb7f195a1d72ef2a295 100644 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 5390 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 diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png deleted file mode 100644 index 46f559f65f11194c94c15bb7f195a1d72ef2a295..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..1783531285bed514517fc0821501e718359dd765 100644 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 4224 zcmV-`5P$E9P)pVGD$>1RCwBb+IOs0MH&WhIV+2R1w^cfy*KRG6?>00cI=8EHbRV= zXv82!#DZ5)K!}PGdso!hqu2{ZEZ7@jS40GX{e9=Tnf+wr&L3aqo-^~7=dCm6Rv&-- z@lQYfG=Kj5KmYvmgAYD<^UXI$jvU#)fBz99MqG8(RV^(oW5T9a{rA<9OD@@_O`BhT{q@Hmf1Epa?)2%?2|>6S zGiJ1J-+tkR7ykC!Z@>HQyN(?@cIwoL#LF$W+^ku%x_0gQ`|rR1_19mXw`_slcT+;r1T*H~i>U_qoYv1RquS0@3~zxd({6Y^CQwGI-@G<4|DHP>8o zqm4FdQx8-qO=AG~#oJ=0^a}NmCIr9`B9kZ9U;~>jUAlDZ)@`MgRwDY0de|E|vfZ4L zy?gh5>Zzw5ee_ZA+grtK{8A}d1M12vubeh*+D<#| z)S*KMjOoFGUfCQBfBEGXen9Va*IgHhFvE%Y@R(f>I#>@F%Pc?y%Mi4z$ck)WW^`Ju zu)+#_k!$mo%C>Ri#!a3)`LM$dW9gW{AdL;Skp@=|PdUi1v(7qTqZS_C62e;(cn~5i z{bC=VHMbz9^vareG2-0dn{U3ccdMok<6=4KKVolP?~)73q0W(HUY`p+qg?r4^JYEx9D&KPZEjSZMWUU7hhaSE!%Iu zedo@d6Pa;ddlKFx17OE`h!l9R7al}Um{^8i;4GX-V*%9dBDa-+FlZH-F!GQ}etDSG zRf%~1k8%8qn3-pD*x2v6&3J>#bHD!d3Y&PtyuXiFjabEVi>!%0&$BTs;!h3J1h)w8 zH`0_aj#yB^n6X&R6G;+6o&zRylWEFtBV4OS`XEH;B=|jyG!Z2VXM5wV4OS#kMvqJ~ zykfqjIDhgL?nZRjKscGVS-+w#-ZmC#!YqKr!50|WUPagi(6cS`62l-&BEg2}M2Q3m z-7*Pja})w+kt0$@$*hc6`E4a)k3Wi0W1xaz=n_IG(p7pWT$9$aJx`i{?ILu_MQjws z3PMGK0%t?AgjtEhLat0u^p+f)J@FjldX*eqNJr_A#xfyUR3(iksc1##Qbb#`6pfR) z<1}Ft!X--3Hn&nX8l*Xh_%*QYyI`tKlIQ;JxR-#WK;4r>C+^n7WyT( zf^+V<=We*+h8t|K0qx&<>n&g>Zxv1aso=0zE5^+dG-qpb)e$p<4msqIWtUx+X%$-~ zHeFb+OU6ot#BnGXFSc&DBg2Gk& zF?1tS+=6l_N_yLDvkjstRp2@4q?4X}^2t&f6OzF^J(ORdYG<{Gwg@6CHQMZ@>GU>? zqC?!7Oqg0(rDx02yiTh-y&kKqvI?g`6xUeSpVDB`(gGeZrydmWmT3f$SLxbp0TovT zTgiLr7fL(ZfH_E{i9t*FfP@?vyz#~x`|i6hbMg{svyna8cu*zxq*qE~6f*^wl~-O_ zM(9uv_-(e?W+a2wmRoN5#1l_gj9{WTxfeCvaO2Ft>4+a;qb`b+aD-rNB&daGj#ni1 zYLiVik} zY1$nQNh3h~DHoFl1*V4zY4+ZGZ!}+h_0=4J(%jL6=+4VkTFFFH#3Bk`+kgN45rPL2 ziN@q_T2h66#C5QBOm^PGm`iE+d(@*>uU=3jz31)f!OG+#C2Tb0ktR+Px4YwxJ1F1C zj8%G^=6iD$<9~^rj#}nltbP4Ab z(~v%mDSi0Iz<~otj2JO$)Tlmv`e4KEum^z2g%@6!ESh|i+|45?Oua@*B@`G%BWb|M ze(2tL=bh4S`qa+#P^8Gs7PKTmI*?MESPdIoQ$Ua=cWh^)oXJ6tIp!F^@UYTx`{gGP z^~8cIoKU9(#*aV#xaehIob@th3^EI&pF8n+dBhc0b-*U?>q@<#U^adMC zu{};X%9OqzBipWEcJn{%H=J?&acTZEnM{NNZ1sd58lEmkC zk{DNIwMmmEjTtjWx+5j!Sb>ofP2?yiNOI@TG|uL7BbXz-T|x{ba80I8jnk9? zEPawtXv9@I2h#CD>zJ82$jj-}6#i?S1(YUY@ct4k(WcnGKmUi=^I<`ZwQE)Is$_*K!uZEbImm@sM&*A@;6UUMvo}v$U;#W zJeOywh+LB4sHZiuQ(s8cs5n>3w*WuJZax&QwA67JCIbr)ivJQ3-W}_PH94A-OP}1VsuvuT0mRvOuhv^~lKx*K!kE z`r6#j30MIS63@W^AzUuI>@wc%l;fc|w^6}qKmrWj^!ewXlMHX-Bcy=SKm}1&r3cQ> zjZn$KuXK>HIv8@JL(neuurkIn;g=c)PLsO_zlx9QCT^0bL>cauRUDSVs0d6T5m4Z8 zTj^Lm${}9F?YQHPuuZ3u3)t|cdaiRkPsWPvau=c0^zPkT&Kx#um^Aj>bI&<%=tHD2 z(p#JhS7g~&ny$ni8>Pe$$O}yclQE4}At!_caL=ASRns7WH$oPd(-6}BjGz>~?Y7&F zJMK6wfX*beUh6)(;U+@N-mAsoNh?3qf&YJ$U^-6_rh88(utn{k6 zIL-}zB9@Bg(zKSvDW7BJxI`0p==5}9g9i_`mxm4?K77wT_vC_tQ32wo`X`Q}l*&Dv zM!_Ow*LuXtY|_9cW=7N;Trp%$ht`#AQ46S*T+GaRC}F}gbb49^rrCY>-N%g^r%S}u zVn~Q;Yvf|Rkw)eG^Us&x`__XHWX%Tv+DcT7n}%Emae^G0Xn+%q6`Hh(*f;W0ofQ+e z{pxYT2`3zM&_Mv;4Vr`r6DEKySqB{k)zc`~axt9LnM*Idlu97y58ME8s^Sto)2p{; zKQmrpVm0cZ5*p=}#gm-oNvQ#nqt%Va9(xQhsx(*Kc;k(8HlP@N?z`_kiA1BW{gZ-m z^~y>C$Yin{q4AA%B2ApHO2p>Y$s)GI7|7)UA?Y#)X5Ou~JJUG6sO>C*VlLH4Y?%T) zbm&mBvdf7lo(Om}5myj$YlL#QZr##{V4qqP*K9~rZZR>2hpszUaJjf>zDI>^?poNi zeD;B5?qJ9&g%TyVE$pZ~aT@I98{o+r;+&0gvyuC#+%J_jxk~lUUAh-^#Vp@ru#70V zFesOB39-x*gQ=kKfEgmrI@(C-fYl@j(hyf-BXLfBIfSR3jeS%8s(j7>X1P;}hsTpr z^cg7mAYE0GQ$Whm*mh9zJg^$8=`1gf-;xfWGSvL)7J2+~MG{1N;yMr{O W;b9zBraI980000eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 diff --git a/assets/icons/Passport/passport_DB.png b/assets/icons/Passport/passport_DB.png index f813f2afd345e57924b6a46aaf2172f6823c67de..69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d 100644 GIT binary patch delta 706 zcmV;z0zLiI2JQurIDZ15Nkl;5(?#D zv~htv4nV-d9K@^)0%me<5#Ize&B1}Y*n?0E8-aj*5C9jnJbwWQ!k@UwfQ>*v=8?-P zj+ngT2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${ z1wa7+6aWPPPyiGFY+3Yxqo6!F~AV8 ziy!4bb0@qb0)L%rnJVx9FxfTB%C85YsXYQ1GA_C0Ux~>NW z;gaYrjejUTz>+kl(V56PIstBbs(?Vete;J!O8L2_L}#3UxB|w#F_ik}$qCS#ChG^) z*q|Q2*W0kY+^i-e5l>`}jgG^zpv2kGLf+wzflj@H9aiY0Tpc`_otNG&=#FeMs_Rvk z+9&36QnT#*Fm)eL7-zH&v@fcMpn5_28XQPmE<2ao?RLN4hdxF3GRGTwggg&ZvO}6` oC%mhx)7!$^)2FsIeteJM4=9genX?&?VE_OC07*qoM6N<$f_@u2jQ{`u delta 809 zcmV+^1J?ZR1=I$RIDZ2MNkl8%vq7p(O{Y^mj-MMz&IRWW8dlklb>M}APO&ha6JbE))xS3fLx{D2mT$PcjCsp6u75dCa(gJ zg$7PRSPT#B04M+ofC8WZC;$q80-yjW00cn+PyqIVdff}UEd^jN2tW-$4FI43C;)&0 zpa1|0fC2z00DlSqpa3WUfC8WZ01AKt04M+o=ok0&h4Aeu;6Acq%a`(~0jL3}0Z;%G z@Nf=r@h+O=aF-1JajVbpY;e=0xBM_mlE#+d@(I zXy`|>Bpb)H<`DiDYv&AilG_Q`jQyv4b+;EGjywBwI@!=Wze7mT)a63NR-^sKE^VoI z)R?ikoq$ccROX3yh8r`V&u!>kr%92RWrT-uFLDyYA=T)9ZL^C4M!sP{{#ip~od{G5 z!KOYGM1SiRGrZ}8zEHd`^&M`hr8p^-6^ch+P4@%l#%Nb* z_9w1klb8`Z29rAYwoV*V_sU}`x$^;k2=>c}On+r(1iqr(p}lcf8 z`1(t)=;A?jh;Tj}4!hm%BL%xZ_zo+ZoQUJ`xZm%$+imK*R977gDhrX7tlO~0!6c@x noFc(1Z$gauzl;~WM{(gdOXY*sI7{g=00000NkvXXu0mjfG$wR{ diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png deleted file mode 100644 index 69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 zcmVpHph-kQRCwC$T+5QeAQ1GhYL)A4uO4&snE(GU*N|mHiNMSd0(qFE z`(PmjgiO;j1C|`x4u=k=;C=#a8nOrU3@w?y-76AbKix{SWoYQ^C9rJ{#u5tUV6<_8 zJPts>!W_h`3<73yZV}%EGtI$)yV!$J3>$%feGmW_v^)U_!k@UwfQ>*v=8?-Pj+ngT z2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${1wa7+ z6aWPPPyiGFY+3Yxqo6!F~AV8iy!4b zb0@qb0-yjWVBZ9MdfKp60@m_3)t?S2FL}LQ&*$@32VMB~p=(a3)A4vT1r1$TG7DPu zzlDDZjMuVQ!9IYw&TQ1H)>JZ5IzBrzzfUy~t?;a%Y`)S4`l0TW+{Qt|W>)~MXDrNG zypRUgj(IsKeM$G_sX1*$BO~ZU3vAiexK_5GZX0PFkpNZ!ZPv{xY!|v@9*>8aI-Rkk zfCvHa_OIn;7hsPj$ij>$fF+F1hpstx{T+JqADSi&i0?iJTWB0r7@oLNYcQw}#-;%?Odn!uXXn1NGwwDdducXilkH|T3sRc7oXHN^otex` zx|<+gYM{iXSQ}A_6)Dn|`cV5&MN!i>u~u!cO@*R~lwP10iV7+ejJTf3W@8^h2WHMW z|M|cF|Nh&Ir@FV))vm8a5Tq{A5lzF@4_|7n4}SOE9DEKg4Q?#!_L`bolx%>ssiq9j zL_rz=X&|X1L*IcAg80VtOxDdNyF|q-_$5!rUn*D-jUb`ck|imFz(r*+pc`TA_b*Oj zsIG>wJ}yZntq92J9iukr9qrC2ql1c|Vy&CeP)URa3c!`nQXy|RVkwL**%jg56B8J^ zq~Z>Su?nedGKEG=8=$oDEeAQQ#=HZn?T? zpq@oZHiz9X2B}tJC|JqliebZ9&J@fUQIafz@{>fN;03m%?YL?1KaEwjoy>>@h%|7_ zVOxO((Kb}UFnRZPXBk1k zC6459PE|OC=Y#+$D^)wI;Tb*{V50$=4Ymg|S27e( z^{qqS%UJ5ZSTSM)$u;eaY38eKkjj~^>Euicr5QhoKAn^l-S8wWUX?0AM}e*H0BYPe z3+PgsMST?t0TswH3yUq#8jZ6or{IF1ayYLBfJO#cn$ckR6});+`YL@2B`^%D%0^Tu zUgH8v0GBAB;grBKcu-aVF7YH6)Hq&c84@EPMZ)WxdpfBqg6-*TR|W$fRz?dLaGq>9 zIQw7fc?AyLnp9UNRxX#}_W9`_Uq+CcM-$PuOlkhaJ9G8Z=p&KIa#;^Qu&uV`_(MOx z-gw!?$2OjN()aN#!o((qUVBD;Fx$UpYHfd0XU)a0YfKzf6rOfXRcytG#ws-q!wyIYd^)Aa|e~?ZxbMHO( zobP<+HBWaY+c(tQQG+1JhWO@43a(!GQrCIlclUh%18}+Bjb_{~Gw0?d8z3RolmQyo zr5=z1l3E=68Uzr;Gp?mGZYI$oDyHt0$~xYHZb54V3A7e0N$CeJDuW))2x5z~pJ1q_ z2C;4~K_;v)=+!ol*r019nN~*n6+y*X??VFx5d!GImC%AdU^rqSh%MoYa93L+BC!H&ILn!WIU@>^MNnRn(Dia)OWKZ`0{_!kRoh7yEkLAzV-DF0 zEJ&`gY7CQibw_1I$VPn7)?ihnfrzOL>A-N~kstYg#DHcuBM=At{3xdkwyy^ov($CUN4u)T`SFcE4rB9&*hGA9N zhziB$IG^IfB?{zlN?;k>FDn3-c#`wyI9_EL5+fi*qTD%GbW&9W+q1k~84P$>87*MI zd9vZ)@P{U!fJ3*gvm+fXl}d2?(A=r*2(o5lJQ7M5zJ2AT_}b6V^`7a{=!f+aKwW#{ zK#*+ubL{$0C$8n$k^m}!+ z4aqGxKUFwYO9kHiYvbahk39Cg+BCCo%lQ|M%n6$g@0tAQ^Oob^{JwL>IUGgu*GwPc zUM;p;E48 fV|H=hUc`f(xyf_n$$93k^1mt`O-5$gvSa@Nx4y<4 diff --git a/assets/icons/Passport/passport_bad1_46x49_sfw.png b/assets/icons/Passport/passport_bad_46x49.png similarity index 100% rename from assets/icons/Passport/passport_bad1_46x49_sfw.png rename to assets/icons/Passport/passport_bad_46x49.png diff --git a/assets/icons/Passport/passport_happy2_46x49_sfw.png b/assets/icons/Passport/passport_happy2_46x49_sfw.png deleted file mode 100644 index f64e770e5a038162d1ceae38d04ff043319ed581..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1328 zcmaJ>Yitx%6rNHagjluO2sA~8L5mM&=dm-pJCklnyW7Xw?z-8ow1|+-+_|%zwDWLg z*4=JQ4T2E_{h_2)G0`ZMkTi{u5Qw0Lw#J|dQHh8c!L(6Iq@h|9+oV|Uw%y7P!Xz{I z-gD1)&Uaq3Cmw4kSy8?M!?2P_V>p3U4|IFeIR98utqxMUo{T%Nuc)W*Lg+D25|xTJ#Dc$Ki_)f!x`O zDkj49i_Xv~NOZWaB~nx-lksG{9@9=yj35XU%~C8&A`Q~%He4x78qWHHg)nr0ty!*S z8hGBKB%5hBNFb^UG3Zt_x@6dJ7Bhu%Mr9?7VmvgZ>-oUuwH-GB|EFWHw{0D zm3rIM@%c21+AS#f6e!CaDz?C?EXi>^AO%6;Nx$NQDjnchuZqH7z$-VUZ=p|-1chN0 z*oc7ftGo~RNQr?e$q1a649EbIlAq=SD(_b~FHKO0B9-r)n>wi=LhYH~E)51cER7a4 z&^*~_aK^uDI){etu6T2@Zfd*YjtfxXp%eGRQg&lXYD z>C^k^p0%q=)2sVF6f4_;?|fQ5nD}e+WMi(s`rs$KPR)#LId=cV_R~+^*cMxxJJdgV zw0&8_+g)6A`PQ$~Hy$Y)&plLe;h6CKop*03e)#I^J9|&;h*gA2hyRHmn118qjZ-DT zYs5z{4S%Iqzp~Og+&k7g_(c1f%Bs?t%uxEZ{r1o=vmcyD_N6DLe{8kRrvkelywvq; z`Q^g{dk0@NX5Y;``_suk`^t}iYXe6{)aRPFZu@fLk9C(o@0F3?Ce_;EL2>Mdvm3V0 z_HUjky{>&ex-K?+CR4f6soHSh^7(%sEv3pv->1q>-LVRrpo*u*!||g0KP*xg3lG+| Gzx*E~RoOxS diff --git a/assets/icons/Passport/passport_happy3_46x49_sfw.png b/assets/icons/Passport/passport_happy3_46x49_sfw.png deleted file mode 100644 index 7aef17674336e71cb1cdf323c1fb039e0e627d33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1348 zcmaJ>eQXnD7{7&~lejEf6dZ`TCJQE*5i}V=Gtg{i3Pxj)$VR4uj%5Br(2>QcxJ9tO-PXw;3zyuz@B6&Z z?|FXD*S*$I_h|8o)hiGLDGt_15x6?wOBF4H-?y&BYT;6D`y1`erf#QY3m{(2Q~(-` z%S|8xWUYP2H^7Y`%eswdqum$|iK-cQ$T=NHCZ2?71aWW5BxN-QY*YbFM#6(l4~<}` zp?R>UxG)(``arW$(_w+l9d%K)Bc=)(wrL~k&WO-J9N03NiMJ$DV#b5b*%jeFCnhj- zPQ{LSuz6CA;Re)aS^(u86t0paiSmL&lNDK2lnp3N(iB0m1jXVcDKdh{vgpEtL3fs> zixDZX;0&HTShH;>MS@7D(~dObFs&wn5(I%DX@aJ4sDY>26Skbe6RGui3ld1FmXWj# zGlAwT%8J=)doW0KK8AQQ99}e>NG)Uv=8VY5NrG~aL_D4gY)(66N5KCymefu~+mnEZ zfRx#4sjwjW`aBpW@Ai&zija+1ZyB&Ea*JfDt#OdBgOUe>HxA9vM4bc*$0-`F0Gh{H zMo@8?BRQPYR8HkN!AUA=-p*2ZUSj9~!%3{G+DlP>pNr)J66584924*d=;}N+m`K@j zLIru>2K2pv_1zXL`Ya&ZrWG~KmV6sDG@G`WYBrN7%{WN(p|GqPiJYV|SEc!&C14qC zKnqxA9Gy$EXe>d&sR2b{VX*~Tr*W3$R9p}=4(Bx|&`B3dGdc`^9{kf_iiuqr*cMf-r|D(OMvukwvy8`o1`tw&u9$(pU5#95~ z%oTI>8uxp$)MLN!$|u8Ts>&uiuO7ZWBpiL98lP5}6NArIJQJ=spqB+Br+<8N>Vw_S z_Y<{yCf_|@e7Vx~+0Lm4rC5)>;nUic6RxqR?aGg??}}}We<5Oh`o+G7&-IS&?LRe} z-r_uRFyy2dJyS4Wz?o~hpirfB!b{SC`cogKU3Sa;{l$NW9Rh4&#f XzZTxR@26EIx&KeP|nH7{9gXN~Cj8-Povj-o(x6YJvi`=jjO?tS0q zeSXjLd%kXOXGd$jZ>*tz1;sM_Qd!4eFl>lMknpC0Ehz)QLuJsfn-T2l$x|4r zs}byJHc2M!D9Gwv^_75cz!GpAYygr=OsBo+n6}plKXx;O?;LNd??=*HtZuz*QXG_H@fc z%N8Zs8uB6-q*{r=u#?F}!=}5CDVQ^&AlU@vCkewS2R5(mdMWTfjU~0+^so(x6mYE} zM}Y;=)>XkUd3SbHR)lPXryLztMaqemGGqYLONbE+-uP8r6*LmaEGw(H3}_B#0=$At zEXm@ms;~;j@5raV{)DohoSTw}2aWT-$McF_+7N=XP*o5hNlBs~I zZyowxz_NG53Q-40p5>%1D_3oU&aCBGZq~9;ARjzSX5S z@A;>FOUxLP5U06J+c1lS4&rqjjy=>tKRJ^ds_R>{c8{$}HYhrX*>`{7SxW0ObGYd=rz{>|;) zcDd&GWRr6A;8aunL+7upZ+znLY-x|L5bAlUr}giN1Mc~myY_rmr*V6W_KxGvY}@kA zrz^ML*p=RQVps7>VtDoM@An>K4i85A-!0{Cta)91@|#yO%V&g!>02KZH?Da5R9nNw zM=qSXxay1X?wu!p9KG=E>G9bk|C)onm&TrzuNNN}x&Gpx4SmR=iQ4_OTeDx5e@cm1 KhdAESxAQ++sKGP< diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png deleted file mode 100644 index e65da5b0e586ab706b40263b637f0fd3647ac5d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ diff --git a/assets/icons/Passport/passport_okay1_46x49_sfw.png b/assets/icons/Passport/passport_okay_46x49.png similarity index 100% rename from assets/icons/Passport/passport_okay1_46x49_sfw.png rename to assets/icons/Passport/passport_okay_46x49.png diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index 2528ebc95d79335975511a384c70c010d476a8c0..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 100644 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 4862 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 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index fef503263fd962534e7e5543954612bf6c1faefd..380a970d9004cba5520560fd9aa24aa42924e2a1 100644 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 4882 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 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..34199910945376f054daa0c1738d7e64dc410421 100644 GIT binary patch literal 2681 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 Ef`SCamH+?% diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 zcmcImeN+=y9!+ao5u{4RRaA5sx(ExC`N$-bj3$_n$VUkvvWQeyCX-2+CCP+jAc4io zTCG?Mt{#hG!Ga&HhzErAqZB!|3a)DvrMRvHWox_DA|h?~cy_C;?gRq5d#vj}n{y`f zW^&*C-FJU?-ehB1N_?RIEPs(m6quNxO&87<;ZXQJFMO|vU*0dACfO5~J4K>^Y2M>G z(a!3bBGF5=Y(^HJrB5bl&MKyioPiO$t#$z|5-p5%+bKGa;Q<3yE5Ey~* zc}h_2EeK@k(||b6!2mKb0?`P90fa(~%5YqU!~htAAuu9^Q4B(5B!ZJD0r)`AD7TI{p4cVOGV+>lxNjq3O z&vG`v%Saix0$vFUN=KJqwU5)CWtj)-|oKapyz6p$$;u$3aUm19L{!RP-!Ry`D_8IeE%PGl^OyD2NiXtdW#63}s*l z1!SZo6hupL8ZyWcDI_5lb-=g@OT!CeUm7-`bPIjoeBAJ$5l8Q5+!d($ki3w0A% zr_j10-}AAQ$@h&cEHDx}lA^s?SAw*+$&3;7-DaQQ-m~c(rFG>p0_jtlKMHelCf-Fk z7`0h&`hSKC{yFhZs_^O3pRMu#N9jIW>0HWYW`vCs2EB`cy<5y^Q{eyZ*Q0)qWkxNe z+1pL0&jt-;9ydhwaaFfD_;RXU5RbgQtag7C2 zhiG(KOrnS*)EX4kX*7sj5~q_a#t}rJi_^#+n>n(QQ9*%bx71r}g zOh?Nr0V%D+vkjR^-L8r$uG*D%=0_JstSCthwRNIZ6UzYqsFat{*<}NghoNb+7R7YN z^|kN%>y`Lk?$*NL`?8^w53Bq;UQ8~MR$yhHIfmNh{(!hK6YktHA^)}ZHQ^PW@~*Xfnv zyN6~6#V@*a?tRU-Y)Q5wKX~|}3?sI6m4^BC_BFJqEC)q%r`~OT$$YnRQ_CMZ2ENIB zSl)B!q*-I%{>kmC9VctbLQkruy?m74mCYagwq{Ri;J}Z7JbcyMg8I~c&%6ipv48H( zZR>5U8+0X?JC_bM#2pJOxZWB#J^jn?bJfX{FIk83{d9;cX7x>YYDnoE^-aUI8}RgZ zIBn9citKF!AINLkL^rrTGK91fqwf0=~Vsi8CK5AM0vh)#dnRC#A`T7`eDX;tPYB~dd1 zep=o%u`*1)tI*R@76D5xM*-QB#zTG=7j%O3(`FIAIS2uN{Aln&F@9)?Dgd} zcc|l&fZy&dy)n6`u6{#l>hr|5n%Cp|4{!DxI2g=S9N<1rj_Ukn(c0 zH*)b`%3H3s_r2=MII*qfW8LLsxV-L~J2pzaJNj2i-vtbLR7>;!-db4@Os~B@@7zkZ zXT=LO^0)h?M_*gE=d1MHA9+sji=XqXuRWFCB`NV>4_bG+XE!Au&4Dh5v>)+5xA%Q< zt!wkCD*1qFcbm2QU-Ns4j%yP)AGv=iHmi5LIIl#~eyjM6MEh_+V&0tvJ5=S=dJOTy~%6^nwZGp5xyf1Iy3Yx}id-~MjWmBzE@l0x=}tLFY`$&$eh z_Sk*5-$8w_6 zvp|`kS>timtI5s5`bs6(^a21c5|2dMSRj!gGKJ)Y_s0Q1NN<*1uyf*yxc=Y@PQcN) zXYNI+{}lkNK<91{bjcOG3t+Ab3LDl%M)5j|iV0<+9BQw2@uIS#Mskjnc^lo1Iuq@1 zPUojwf5EFuq0HIYjV~J&lbMs7EwomF2q9smgo#{VKo=dU2k()Kvqsf6Rz|Rkp@uj? zf)kAQ-duG6Fvmd)38y|c*kpS~0@wj^m)arW`r(~xV~a-v4Qloc zqCkTr@Z9bXPhKDi0Q_lT#$ezHA29RFLZ1uhDNO8_0D5u{6mkPGY(Ux(^F!>0Zvl6Q zD`*w=n)g5s-4LwCSyRjw;qqGDoIRqF3kZsJju$X=2O`ppRhj^h4m*H6DALRUvgF8t zwpR}ox{)KxP;5XABe^;CRJ(>~S@LJ;;Dx!N5&4n#{x6I%5=RBSc*ek)sTm3)s7VgX zmi+)Az1H?_TgM=Na$;`KvB>+i#8p)|a0j+^)F8&It&fZe{kmjgNP8kn7o= z2UhzPo&|ax0iVG&SB*ZNoHM+iL)W5dI-7SW2*rRPDnlH|&h~1ud zzEx%ewyLMSK_{5VHztn=PhD1@mrL2=Net!#=r^ouPQTv!is4@q{*#S8n%}uS2rvv^ zRJ|j;F98tr)>Nmh06=2(TAZOW8_=AZKMDX9-zCpIPLivaf;naK3uZA)Ocz3+&RULfT3A`<<3VCF zHQ5*@_aXM^2$31?^VbF6`(KTZH+QylMm|=YQ792E=XT<`b{+QqFgWqCb%|oA@LFw% z2mD36vl(6Kr~E!L<1d2$b-^OJ;6YT*pVIUn9vH%`lWV%uDp z-O2qV3V#2L1tBNM24Z)!P((8@U9mYs9LdO6&FlJ{j`zl|_&}zpe{d1{d_n0uGOPFj!5}QU6d-2ER}+!Sw7`00)CE+LL|uPaOoFo_Dn;yo$n$ zj`xQ4R`q(!Qf9?xr+b-!N=0lpH*Q?6ZZTOL{77ufnkzXLl>o8npew8TF( z&}P0w_?5RL@q^jTY?C_<)#TQWm-2_{VCS0RvETjf;mg|Qt`9#JV)&FN)cWMLS>zoq zQ~G(+dsHh#Lf?x{I^aRq{D$lI)5Cb+)%)^m^XtZ{jy5OxrF)gPm2^;>ni^F4={@wu znONPfOuuYQ)z^x?&6voCrkQmoqqU>!Q+iXv7+fh8So6o>&HbIMYTa0gh9~}(YT}SN0;n~y|9_h8Qz)nnLV?*%a~=wWx{f+ zY|{Z@+3pk66JZo;U{jDkxneqhSo1?+ZQ+M0CvO?=2LV}&S`v>=#WRFcgHk2z2nDwb zi1$Mo$>Y*L%iCXdEahdq&FC-sO6VMDLeyP)x?!c3A=Bvk0l_r-HMinCa-nH7t01G` z{bKwiQB^u*p}E)FHl!hy6Iys8{U<%^KpfD-kj+3eN74S zru+J%{joC{Wf5gh%C3amTs56f6{dhv`J#6Ha|;Fn$eWYy*+B9xotT$%-so&xo`(t;&tbwS7@qc2e)| zM3!Unvd+MXOUQMf8`frlA2oQa(aN+0sk?6!7Ofmjy1&s|zh3yNs+*LVl$nR-!>5!e ze}R@BDYf=eR!-y_qfHP#$t2$Vv@)i!7O>bS<{xROh>dhfI0(MEF3Nd9Za|cHG5>7xylkGN*m1B>(T}>P<@GQvA zI&FaHOzWquSZ=2Ix!Sr`dLvu24J4>PElb^28W+D!#<|W_kbi+!Jo*$%PZc4G5$56K ztzp!&v!rR<3S-tuubBGFYWhSQv*Yr*Z%tRgm(?$7uJ>t`%#SlNr9`EH17(WkORGW4 zvl79)KP~%RL*qi8ZC3Se{n)!M@Z)mH_6uAMVQAI8#=Z4}ivW|K=VtbH(oVBg6h0Xo&eon?)^smzZVD^1=E_xwIpjhTAraZR^%owTf=R%9g32agJ+;Os(A z+harhu)3aL1AUNQ2%KerfTLnSAq0P706YW%{?jg;CGRdnz@R@>sD23WU!2{qO$ba=6$*uG=)yHML4QABmOF~47aVP3 z_O~VnS4j0Sdn@nBW4i z6g-)VClNur7BL>AKq>;vqWV`11hSRYKZc0`e`ku7Ge`)A41uXZAq2v1V1H@{P|>*m zq4BTU0roU94uZx7kOC=K)`55_{06g<_un1uDzezXttqVQ0}vll+s?n?O?6F>+R)=B zFb$**3}%Wn)>7BjHPMFYXdsPE%(QiXV=agQR16V|`|TUg^8Gg!`CqYcBn5||k|_2h zlK<~Au<;>LNdZ12GDuxrO$&6y3WLQHcUO+?p3+}Io8Tz;Ae^Teg+u`TnPxctUnuWKDPU{wCBG_`5^SrDXDDWsPryOAx% z8Qb*4J4V#bug_E`4$vj4JhIE)WvY#0Z6=Ogt`Q3w6`1fCi~DkUb-dj(=?%y0vCrfm zJnG7Iw>w1ai@*ZiKV0Jrg(|w1gpw{4q0d<=?Kun`O-ejQ8A;&nJdlp?YKe)^eww~& z_*RZrH{)=4+|xO8-gQRoLv%;cVVY;>pTh)G02RCwC8*$2#?;~EF>_GxvQwYS<-QF~XTwM!|XY6giFkr+Wl zjKm6}A~Bl^lE#*Fpc18m)E29y_EsFUTCEP}_rCw{yFGcouXU1h&Ykq>^WM)r{?~Q= zuW{dBHGls6Uw--J#~**(WRpz}KKS6%Pd|Oss8Kz8_UzK7%Z@wlxaz8_Zn@=_g9Z)S zV~;($b?c_TYuBzmb#1n)s&tm0|Jb^B@4mtcE10s}a?6=Da^%PjHrQZ|HP+Z`uf6=- zZ@>LE+;GEVk3IJB#~=UeufP8M^Ur^^w`%d?#VfA3;>3v)zxd*d)mB^0#@~GN&4L9B zbi(xe@4s8xy1)JQn~{qaEiykqHSapR{Kw7UxpU_(S+c}Bem?l%gI+BD(n~M-Ic3U} zFTebfzCQTi1BxSwk(pRE{#DhZk3PEk>Z=0;-)EnFrjv+s=FBlvkF|dO`R6YGhXxJY z^`5W4{`%Kne|`V`_wzFvX_$=BUwiGf_19nD?^&~E{qVyNUJU*C8H2few!Kl_U-G_$}6v2G3$pPeh6HjK7D+iF=K{l zk#a$^>HPNFZ=-ym|0h@rxjk(+d-m-5SL~uudST?SzhQC6f`F}k1}O7M?1{zH*{MD< zK#LExw5r)cYr634RKkwTX1bP58z|p&lr`Q?|fA-ryBg~dy2n{K)(9xuQA^2jYL z^ytxJ?X}n5XrqnRTyxE1jyY!Fz=14l0L0Nti&BmE?A^O}Iw4_6o>6AuGGdDn6Z@u+4+iU~V&O7hC*=CywYFll!72K9R^w2|x3>lIr=Gg)? z=JLd@(j)qO_0?BY<`b-q3EzMJJ?v~v)?(<666^1{K^kSS1}(0)-g;C*k(Q!AMi3EK zS!ETmP~3$>yi73esLr~@_=$j6+@P`+lt^RU#NLjIY~g#yCft%ZfefE`B^_`(JsWSl zald~3IK%-59AKocm(_y>6E5NXPV*9^pM=_iDDv2Vem$C|f4y#a9tNl<2hi1RRv` z$y-qdrs~I9({&CWJeXPaAO_PHpxIaEGsVlk`|jJ4M=HYU!T=yz7}L@at^|}$O=e?D z!(lZc7BWub6J7!;{_&(F+jZAnmtHA}^|_s|q6Eha;A$EB{%=6H1?3 z%w5DyzX4WfGS7~>gn)0-89-imdddiG*Ijr0NHx}0OxE;>|w)(fs#zN-FDl7E(ya; zH{L-^Jf{du@G@e=2r<>fAg&XvksE9PuQ}kRrYedo7Kr({veW#Qsz~v3P0>&ba42P z!ZAf&uzDJ1LL(D2-#g|*j>xIei@+ke1!a*ZpM3JEW&{2D>#w)qZoBO!AOOlZED5?? zFmK*G?@Sc>K_5~fE=ZN3Azwl$-taYaW;ojb>S>y%`F^sX_m<2a2d50%Oq(`sr=519 zi5FgYK^z@7Zro*;U3S9_H}HCu*VL&~FTeb9F+5f5r=NcM#v5-ydHCUn%T@MGeZ&F8 zufJ8WxV@#wAP_Wtn=LwKyV0XZQvgM>h55K&he&@J3Fy1;zKeo4-gu*3k2>n8^Upv3 znrp7P>#n;57n$?sn{Vbo)O^7O7udyiRuk|%gnmtK15amO78nSIfWJa^xH_oPXalsoKOc$`v0JoDm2eLk~S< zBWn&FI@BVdTyn`J0ua!*-g>K|OxU{f&O7xd$?dbxK6GiXw)!E&PvXfbr?^gH?Ad3Z zeZ>`5AU^hxUOXd%epWU8GD&adKOZeDqJRQXfG}v29Cahg&%0 zf9fDFM;>`3Hlyje=bp=a^~4svcmZ9F2Os+|u642|^pNC4p*2nvVC8)1L;N&36BH&8 zDR`gb2tDn+_ug2~SgFtqW?389=8;Dp5gcH3ewAsCIN}KVp_iaT3-;@@a#aNB#9+c( zhe7PifgHOjg|fOeTf@g$CvHV~{MTk7oV{Op<&|x>-S&hNPCzxTdh*F9D{TnF;?xWQ z$0QZg!+pmccjRED-=WktT<5K~-fB4?*BkB%Ok(P_*Ip}lSqfZ2lB#}cr~rz6^I`)- zlMY;*|+7GhO6YW4Bs$3O7E17pXIeeAKvZomC@JP`+$cH)U@ z%)$rT5nIh+Q$W0T1mWTt%RU?%W}_5o@h9u6B}AoC)V5jNkMMF~v)PpqJsUZ`YQ%i?!9DlfgXWwO zFYzXUH>o_U3mmMUpXFGRn6ivk4mz@Bp3dA*!UG8vaJ$~kR;5TT1v79lx}alY`hbZR z_uqeiG{67;`zKGHEC{DEFh2Q)H7tVD^>c!LzPPjlU8*Owrgt@w=R5Db!;I7c16`z; zY(ohX+mSwsOosUU^Urn0=z0*va@OKZVmnr_BTvd^Q3*3~@Omh=W|DQMD50kdq*pIT zL9f30s=0bP3f4$76eF;ugo`P;i@j;FaTczsR1&;c!WC}tlCPw)668|M^0QpQ6<9@p zr8V+Xf2hgT#G@RK=#r**&%*XrrJS)x;bpAv;F2Ss0VxE-2TAX&v(94o6mKF!vMsIW zKCB*PS}uTETn7bVijXsB&XjxO43=U^&pGX_su#(W?{f&D^7G84D4~O@=!h& zl_Xo{>{hpF^i)TdAW*-FFh%FR_uh-dQbWvLr%mHRi5LC`ykP3c$XuXh@sJ^Co`3%NoWy%JTkH_`YRixap8MIH{8qe8qpcUmb%D3335|N0GO(mk!H(1;$>NIVL1pK!}I zv2||J3Q(}5Y$oE)L0}`czV5o~;(5^yf2gO1z^!ZvXs$jIiG(4@Ovs5b1!ak0EGvLp ziy>?oH7m5#b?Ta2l$lko`0E@^YE}Lv8gS|hFTBupifhCpGhIlM#z1Uw#+lu0RSp?~ zwuQ1T1i44Z8JwP|p~#1kY)zN^%vz~;d_C>7)5s&;!^#3VMdjF@5?h0V7l%0t`>U?H z3Ztk7*KGu-Db`O#k-99caRWmWI1wutZ4jXop@hjy!<(W@Qsz<_%G`qH#G?#V94SK; zqbHttBEsQfivQ>`2P{7kR7rpyR)bPjBuOyD8S0MMu()1AY`^{X^~($~mK7|K^9J3p zQBp#}gwW$F9pnO6KcEg0!qbQmBX-zf2WL(vyD?+NTzTb{3M;8UZV^R}8a0Z=%wkj8 z#i-=J=t67(I;mUOZ}@moVLXG-79y(^jlcJHL#ertc3y!}-iO0E zWZbxMx7~Id@;T!=13B-qvNNtol-NSM0|yRFm_r_Lt?4A4D}wIrt*YNzfl@B$4OKue zJtYyEI$0#cxQKNVvtmT}R0;XY=);B$qh=-dph1IZD=ebUDO096^*Uo1mh*m&K6c@e zrh`6UzyPFgz51)?v>eKUlG^DK1#@Z0y?n~w8Ej11dUCmCv)t^Ynuc3d03xYEB+F(^ z-ji#~a`6{Cm=ZUz5h0l_x4`q-Ta{8bc8^1qzh=^zOlY!g;_OTrDM3`l<>EPi|4JYO zA_LH4=+L3LeSGn{Cy;4a)#wk#TK+n zuGdQPdwj!kOOrpmiGN52UnUi=Dir~gn^;vk zls?7HQ#aJhZ_iZn33KF0F&c<02$rRA7p+CB3`=Pjf`X3pDKugGH#)l2YM(OSO8@`> M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/icons/Settings/Cry_dolph_55x52_sfw.png b/assets/icons/Settings/Cry_dolph_55x52_sfw.png deleted file mode 100644 index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3898 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 N!3~Mzlm%~0{|8*@s)qmo literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index dd220bb65104666cb19752a96fe716cc44dffb68..40f094ac9ba758ea06838c2bb8376cf6f28f8050 100644 GIT binary patch literal 3761 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 delta 1860 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 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 495e8ab55c1e2dabb547151c2319f321f4af13bb..68c48c0e68142548919d6a4b02e40b48a243b04e 100644 GIT binary patch literal 3767 zcmaJ@c|26@-#)fNSrR5BW5iQgW`^N*QF&1S0K}{@ z<~ZJH#Cw#41$ggBZpK#Rpm%ERm8u{5b*UVy<_v)2fkBAW_%^;c9MGWU&>#&o z>;fL!KlKm=5&^)Ebepnj`0o?@&{UD$_XLo@x5X}dq?z7sYz;sNp2Fq#WtcmM%+rCm%F7GdQd~{MxVBi~!m%=_xV-$w*08*@+n!uxZ^6 z0P@T2uQv3IU{{kT8g=4|-R&>%+2vjZCyH~0ks$)j!DUboTFE&+Ny-{XL4 z-+~iBuM^-su>Cx=2Eh7{I<_S9A3F)@K~Qny~MD{88fgjzGX9Y+mz* z{I+;N(n|-yS^|KC>g8Aybv~daCGQIWRD9ia@_r)7rcM|D%(E{Xcx<|L`zvX7jd;c@ z#flmbKVgfx$@aZ9hLUDmM#6WC-r1FGD(6sx>=u0XSU^5|N7|5N$t$>?*!ZB-$qSQo z(3aD?vR?11xFRBG+914>pp1;%;U3csX%z;g#t1?P7@eEpdEkji>0^+$4U9bg0yCd@ zSB%P>CPP|3`lQK9TweniaeeK2g%v#U!joeHANT`IUffrbzh0N^-_F#La1Bq+@okqc z*@Fsyo|E5x;`BAVyjs01aTKrX6st>A#2*TjQK-ox zaE15sM}_a45%Slg_w7nlL!_2#gWZwE zad=C%+IQtW%2}$X{u1BmmVntS-gtB5sHn1P9Kw2=wJ)X*qnT%)XVf7{&ahFNjH5Ju z9ua;L&IYH%m&QrN+1*@id^1uijeHw9Y$KAh7nhePby$~VJSuqiHo`15`@yl6f~*h1 z4|!|>CzsHly@l|zF)Ua zj89zp5wud&EcV=#KTigq4z8upJKlhh=9XLKwtjJiDE8z^gk|;>Os)g5v`#KsS+)*An#G%SF`NV@HGFT`x}SKmJY^Fo zw5kvYJ!@l+_J82X_Kf$;96Qs(Ctg=PIHTW_aky2vRjl=_rhulgW^tEKp=V)O;i2A; z-l|^rX~wkd^kgqLfSzfRsgb#Wq#&09M9L4AA1jY2?+a-AX7rvo$(j&ZXj%}T_|$H- zK>3lo0Q$!5;k(&~Lm`)jx9+r;X-?4wep4tnaTALD`N}Jj;t-nZER9&PjwkbC6 zmQw68U>clbFaw$c{pb~wc|$sH3Tg}9OgMWfc-`^OoYw{2I}*o{N(oE>*;Deb=TmQo zaFRylzn6DBd%uvI{xZG4u!r*DQ#0yq=)+Z8t#pM(*EcAx>5rKu{P3CPFPZu2`LE~W z#-XPIPr000t=wbxG(#<`3r1<^t35GT$vJx(X@nxJv`z>Z)fw^M4!M1`>RuIO09B;G%^{gdEE?_4c>x-m zXys%D8M^4Y#GTWNxFwbH#CY@)*bEEZWjC5nwN>0XcR4-6&Y~(OI_Fq%Y1v`Jn`4-dF4_1M0Lz>~QL=c)6P|ROW4+%)36b+j`FQXzC|sBxdB|#K_4d>K|dH z`%3LR)zuTkMp$E%CWVBnO-myx%l`9yGJX*@?YSkmBCNQq4fMWx@QGa~g3B>ef6<6*NOrGfo<#=Q6yu=fy86${2j+ zCQem6QxA6bt+WVTpmuz}_$gN2U7@^sEbLR6Rgm4=tk|=6&u(3`;7IlaPQpNFW9elTHo})X&Fd8U%DVXiD9XC zAgcq|nz@v0)SXK1*BiN+il-E44j zM8DhgyN1LDKU%BmUH`UqMdI7}l8s_g4Q24>>6+7RZ(Jm};v!eGHWGimzdF=m)oj(N zVyO}xo2jIvASPcq=h2!ll>a1uXIoR4(=XPf|HJWt_2JbcZTPkBnQhBaZPi7rf_Vn_ zVZkG0y-K}OMJo$6dvC3{^U z4DluEdq9khz=pv{o&bf!B!Gh{epG*CFbeXwE|NFjG(#cazadOt6y!fa;cc;CG>t(5 zAJT$r5@9eH_>jI9$%BA!*Y+e3HNXfM0uF`q?n9dJLr8=+60Q&a=YsGYGCVwyICIN? z-0@Z@h&Pi-M?#@NK|xwU+FCS*7Zk3quMdSGpa_H}525MLqB04=npA(4-wNg=er+JL*ZJm&8&U{ZEgR5D24Jb+MkId z{WsqKQ`p~uMJGXVB!5}}gUH)EPnAtqbR?QVA~0zT2O7=qcOkLfG$zg8n??sCw6);i zeYON5nYwA&|2M_f7HLiOXA-DHlC?Ps!V}RVlRc0y3sbbEzApM8OdAe|TcAx3>FVlR zAP(wUSenCN+WNn_<}_jeg+yik=6d{>YyL;>Wy=U z(?9C{!}a)cEM|Y?LV3YJH~02`?e%XHuYoqF|28jg@o)Q+sJxzM@S0mWr*npPFt%WA z94vla-#jDb3*|80P-|^&>JZ$^PC6k~3lp=Yhi@z z`51nv&OpWmcVI2xti+M&Q!C)GhLRd3BUbIfdT38#i4Z#bTk!`2$?ZoTJ;U-8%n|y4g+cwH(>YR7vd$GeGzYI#^JouGlr|_w0cCP2=dVlPi aN(~_Yif=!tN3{$L010qNS#tmY4#NNd4#NS*Z>VGd00$XKL_t(| z+O?QlOjSn|hG8oRh>GGZ;swMCiVCP$2m(U5h>bOddI{B*1Oy&HNv)*;Bx*FKeZkhI zB-%dM1e>&xrj7K$)`!xj)v9R=_C!b_pdvRBLEK z*T2@F<>ch7UAs0lHPyw%W%1(0vu4fWapugKPEJnFFPgqDO&e_bx7Dv2om?gSDrp=gE^NdIBg8sLrZYs~}Qxax$H$sHm{8u)Msypr9a30==5a zBGSKg>sDb`!T3UNZ*QM6WyTNU-=G)7 zYFilOUQ<&uzR)2$tR2p2fv%WSS69dWba!{Vy1F(sHO-wnH}-0rkB?7qa4qP>Z+C&CQU9LAEP4HkJ-sfu~_V z#xQ|tn194ZjmD`;NvwJutRN6S306k%AA?JmE}b)Hj-En*yuA3Bc%7INGuVyUvu8hk{8$kQU-Am( z?A*E2_gg=;M0|WaQV3$W#INShpO3-F6#Y?pU4PvzLq~%F#K&+OlmP3qXU}jjzI^!b zA$^&LNz6p`=|o0Gp7kkvzjc3J)4r_#GQ-2enNI-Rym|B5wQC_EAp|r(ApnMEhL_if z=x!A{+yE273SaRP(sAn4DUvI=MK`erNnvN0jbX=+A4ea3eSO!hTNfG{x^?T;O`A3y zK7V`|BZc~%ot>gEBxXHs!d_jr(F4~kaauG$x8h zK>$3BB_uB%OG`^3dQnl)>C>m*ZA;r&o$Oz}hCH|~m=%8D%x3@Q9 zpPQQt{n)^a&OM4J` z;6QeEHXMujbHnhp3SEo72>3GouLSlGo6QwtC}J}Ty$W=pIQU=-noS;AxpHMfLIO6d z|0623>1_xD6DU`hom90k;qSx~KA+fL6xN~fX1L9`J_cS#1c6SZQuHAa7cN{VDJcPc zMMZ_ofH~|5l2?`VkAsU6B)gh-TYnW0=P}|s&?V!b*^*Pr%F2!%I|jLB1}uWRk-x8( znRZ*{>%ZWIQQ~%}hJg%x34G)~qysTQ4ats3d#UH=KL%AGc38#KfFA-K`Re)e=T#yz zqqu{(Rq3Mc7IUgb{Pp(s!U{M8ttp}KG4_}W?!rRu;MPNN1iG9;%oJvP5=&i3>3!?xNjc8YH;|2Z^PqxcysetUaZ|03GL9}00m)2BnQ5|^li z6l6#^jwh8`gsZNBk4%#(X<}kxEO5X$;u=O6-_c_oI#~I0_wHSMOqimNkej+dk>p%> zIRQXX@R|Eb!yEtVe+*o8@qZZhjj4!Vwrp8jTbnIF4l~|P@K<-eW^aB&TOHKn)K_}uPy*Tkbhhb?3!P=Xj2!XFf!3Ut%seSt+u^c>?GJv9N2wf(2uK d6tdy_{{j6OA(WAZ17rXI002ovPDHLkV1l_ti)jD= diff --git a/assets/icons/U2F/Connect_me_62x31_sfw.png b/assets/icons/U2F/Connect_me_62x31_sfw.png deleted file mode 100644 index 68c48c0e68142548919d6a4b02e40b48a243b04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3767 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 IM6N<$g6Xe=bpQYW diff --git a/assets/icons/U2F/Connected_62x31_sfw.png b/assets/icons/U2F/Connected_62x31_sfw.png deleted file mode 100644 index eeaf660b12ea4647154c8d616b468a3098203356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 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 diff --git a/assets/icons/U2F/Error_62x31_sfw.png b/assets/icons/U2F/Error_62x31_sfw.png deleted file mode 100644 index bb280e75121e12eae045d87a33f61b1713af6f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3751 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 literal 6876 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 W#^eyOwOvyH0000ZB8b0=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 diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index 43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d..a299d3630239b4486e249cc501872bed5996df3b 100644 GIT binary patch literal 2459 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/icons/iButton/DolphinNice_96x59_sfw.png b/assets/icons/iButton/DolphinNice_96x59_sfw.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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!QmC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 5122 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 diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..2b4bec7c6f14f53e7362da75f95b83bf387eee54 100644 GIT binary patch literal 2157 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 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png deleted file mode 100644 index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2157 zcmbVO2~ZPP7>*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfdO9l@s z__)F1Bs4IC%YIQH0Rc>WAo4yCJ%`2*B+|!?rVl@O_`&A_2d#}Vq4$T(!SHS{c%BRZ zO#&yRJiMU!FAf0qZvz*)Y{BsXz(M9f_Wv8q3@|zHKR6J0U@VHp4_^cv_CWM!pd5we zfCtGv0rw0KmjEAYvj@6tMdJ_$x8NRu0gi+6!2pVb>i&Nzh(D+P2pB$v;~)4(`~dg= zz$g#|c-#T?fPx>#fDgDla1=fydxORPGhi6`AmH-;gV4Spq5>d%?e6vX1Ke&Qp=XeO z0OC&&`2*+xGo^Vzz~%l2lnxDH1dxzH@gMNP&twmo|A!9^1&}^3_8bHxAAk4`0)7i9 z5Db&X4=4aXMJ5kS06kkK02nYn1*QNLxo`pM@gRFE%pODp2bF*x!~_DA!2YBJC5(On z5C^dU$zwf$MhWcmJ_q$ICiWO50{&GKdyEeOV@JRMej>5(P+RZ-7`SMEs6+e`%m{D~ Og3&$z^CF>$-U|nYD9Y9V literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..843aed27c7df836480f795cd649be62ae08f6ad7 GIT binary patch literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm new file mode 100644 index 0000000000000000000000000000000000000000..ad87a307189f29491e693d9665e9c9780584f1ab GIT binary patch literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm new file mode 100644 index 0000000000000000000000000000000000000000..78de0389efdf70991b33b41f855675bea8b3248f GIT binary patch literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..32bb893cae287bcce9d3d5d3590bc1cd890ac96d GIT binary patch literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 93% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt index 6503043bd..fb0fdb228 100644 --- a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt @@ -1,6 +1,5 @@ Filetype: Flipper Animation Version: 1 - Width: 128 Height: 64 Passive frames: 21 @@ -11,4 +10,4 @@ Frame rate: 5 Duration: 360 Active cooldown: 7 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_52.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_52.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_53.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_53.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_54.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_54.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_55.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_55.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_56.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_56.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_57.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_57.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_58.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_58.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_59.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_59.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt similarity index 92% rename from assets/resources/dolphin/nsfw/lvl_7/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt index 43dd6af5a..f169de719 100644 --- a/assets/resources/dolphin/nsfw/lvl_7/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt @@ -11,4 +11,4 @@ Frame rate: 6 Duration: 360 Active cooldown: 0 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/resources/dolphin/nsfw/manifest.txt b/assets/resources/dolphin_custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/resources/dolphin/nsfw/manifest.txt rename to assets/resources/dolphin_custom/NSFW/Anims/manifest.txt diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a GIT binary patch literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx new file mode 100644 index 0000000000000000000000000000000000000000..9dc1e5dcf52f7721ffe6f002905111825fcda8c9 GIT binary patch literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..03c6304a2d4d334ac519d1861981fcfd0393755f GIT binary patch literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8af359b940e9a14587bb8ae598aeff8e53b6542a GIT binary patch literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..cb92216c4e5943d4d845c979c7d8c59df040b95b GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8932c5ed3ba499c5a9259d2d228330e5f4a5e94d GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx new file mode 100644 index 0000000000000000000000000000000000000000..3eefd86bc994996073b05eee2f627dba8b8bb9cf GIT binary patch literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..22a76dd4daaa01bda8be01f522bf58c0a9e22e2c GIT binary patch literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..75f57367cf539f9f075e06c983de83f6fb826cbd GIT binary patch literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4a569c1eb27507a1301fa34d2ec5d96d62fe43be GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..582247d43e0c1cfbed6e9d8061ec26b7ad417339 GIT binary patch literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..fae05bb57f5b883c6fd532a150899d556ce750cb GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c91714b3f1114d842e45cb614c456d3b40b164c5 GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c37cd533d7a3df21e738cf4cbffb4483f9a551ca GIT binary patch literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..47b611a6ad2b3c2fa21cda23b155e56742d70c77 GIT binary patch literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp Date: Fri, 10 Feb 2023 03:15:08 +0000 Subject: [PATCH 66/75] Remove unused icons --- assets/icons/Interface/SmallArrowDown_4x7.png | Bin 8340 -> 0 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 8552 -> 0 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 3692 -> 0 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 981 -> 0 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 3740 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 3611 -> 0 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 1690 -> 0 bytes assets/icons/StatusBar/GameMode_11x8.png | Bin 3610 -> 0 bytes 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png delete mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png delete mode 100644 assets/icons/NFC/ArrowC_1_36x36.png delete mode 100644 assets/icons/NFC/Detailed_chip_17x13.png delete mode 100644 assets/icons/NFC/Medium-chip-22x21.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png delete mode 100644 assets/icons/StatusBar/Alert_9x8.png delete mode 100644 assets/icons/StatusBar/Attention_5x8.png delete mode 100644 assets/icons/StatusBar/GameMode_11x8.png diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png deleted file mode 100644 index 5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8340 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 diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png deleted file mode 100644 index 3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3692 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 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png deleted file mode 100644 index 9aaa1c5552a2d115b7042a4bee7a0c636ed00f01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 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 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png deleted file mode 100644 index 4e0ba8f05921ccbfbaa4390ad07e98fd454df00a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3748 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png deleted file mode 100644 index 137d4c4d054227f27ad6a7e3e374b100d975d9af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1690 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 From 45809b28b0ad139c9bd855ded2e354a3313c6737 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:15:31 +0000 Subject: [PATCH 67/75] Fix dashes in icon names --- ...ghtning_9x10.png => Charging_lightning_9x10.png} | Bin ...sk_9x10.png => Charging_lightning_mask_9x10.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename assets/icons/StatusBar/{Charging-lightning_9x10.png => Charging_lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging-lightning_mask_9x10.png => Charging_lightning_mask_9x10.png} (100%) diff --git a/assets/icons/StatusBar/Charging-lightning_9x10.png b/assets/icons/StatusBar/Charging_lightning_9x10.png similarity index 100% rename from assets/icons/StatusBar/Charging-lightning_9x10.png rename to assets/icons/StatusBar/Charging_lightning_9x10.png diff --git a/assets/icons/StatusBar/Charging-lightning_mask_9x10.png b/assets/icons/StatusBar/Charging_lightning_mask_9x10.png similarity index 100% rename from assets/icons/StatusBar/Charging-lightning_mask_9x10.png rename to assets/icons/StatusBar/Charging_lightning_mask_9x10.png From 8376e866e380d870b69c648e85415c3769e9e4d4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:41:07 +0000 Subject: [PATCH 68/75] Use lvl2 sfw passport portait --- assets/icons/Passport/passport_bad_46x49.png | Bin 1237 -> 1295 bytes .../icons/Passport/passport_happy_46x49.png | Bin 1296 -> 1328 bytes assets/icons/Passport/passport_okay_46x49.png | Bin 1244 -> 1281 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/Passport/passport_bad_46x49.png b/assets/icons/Passport/passport_bad_46x49.png index 9b0e7c74ef20cfdf8b046d7597bd857ff786e56d..d11682ab897877c902ed5d77c9804cc27b85c529 100644 GIT binary patch delta 366 zcmV-!0g?XI36Bb}Ujhs>H#jylL^d)sI6^p+YXUF~GdDOkG(;5ujnstB8+i?D`W&%V2!>AU>`~W(!FAFSKyF5Q~--7`iB`F743PH zw9IJGinig;INPPTzF5|s!4`NY+eiCi%6}r!X*(O6(qC`}3ALxgi2*e}-blrvrJm3%%mXcOM)Wbqb9CI|k;4$eu8eF_=}(9Ekjz$4lfi5%!wSo}80&T#Pcm0H#{OU8 z;N#FVU8!5>DR}bB8K2us=24teC)nXeu*V-k7{lY*Q6D#S0c@!(N=4C<7RAVf8$Xjw zjlRS&|7=CiAkMs;N!F%<>FYe&{1%VqcZn|z`+(=F|BS!;Kl2e_0PY(Dz?oR1a{vGU M07*qoM6N<$g4ctiQ2+n{ delta 308 zcmeC@y2`mBo{7iE$imdz(8AKh!pU)RHj@ENzyc~Dx%mK-5MzCZa!o%20|Q%tPl)UP z|Nnu^&_kE&fYen_7srr_TUV}m^Bp$eV7V~!cl<8jkkiL__NUJE)v^^RJji+A;31Ft zy(cz=HP828Iq=YUH}`?B%xOCrW>oKZDxCI+#v_|ez4CU?%H`~vG2@L+PnX~OG&+G^KkipZ{kJV+JQ$iB} DZoi8o diff --git a/assets/icons/Passport/passport_happy_46x49.png b/assets/icons/Passport/passport_happy_46x49.png index 56ea000cd03bf445ff64ac2a9c01318a4ed280e3..f64e770e5a038162d1ceae38d04ff043319ed581 100644 GIT binary patch delta 377 zcmV-<0fzpN3a|>WW&!~=lV<`u0X36n0v)r!0ulj#=+R5l00006P)t-s|Ns900033O z(|!N|0We8KK~#9!oR)zSgCGn;lmGwe?KVo=r05*CQ>PC|ONjz}(C~uS2U!Ci z>5YSyxPeX_LlMLGP@DLUrY!RfJ?tW*f5XRZK7|)>sg~oGt}0+1COi@Ocfxgwlybt| z{Ghm0mv!y<*H>`XXE=xx-fZ9+->IyNu7_@5{F--K*}L(V8nI*Stg*yqTufVC#qH}M z-eZ>M#O3g2Lc^{)AqdjE|Ju(t?||ENz~4yr)+j1+N0_a3klMvCwC@mATa@ zqW%)#+A(i$3h>=4?GORmW>2q(DmGFQ^Mi;3!}&U)=Uw_q{}`W#RXGoFR~hOl8^6|F z@U{G=30TfkG(5(|hU3NV@&}&REYFEHy-t=uMpb)!X>*%ijL4W?#c3Ve@krJohE0PP rM@0QR_^);SlenCg_~#D=F98Ms0>uN?-Jm^600000NkvXXu0mjfAxWH1 diff --git a/assets/icons/Passport/passport_okay_46x49.png b/assets/icons/Passport/passport_okay_46x49.png index 198ba5436018163b26671d31780fc5b8535c0f1a..34fd3767b92bc334f15e4e13811020c1e0211078 100644 GIT binary patch delta 330 zcmV-Q0k!_z34sc*W&!~=lV<`u0X36n0v)r!0ulj#!%b|O00006P)t-s|Ns900033O z(|!N|0Rc%wK~#9!w3bT(#2^Sm!~HKktybj|6c^*73;Rq_0v3>qfh+pqQ=L8vxW?QH zB=NN;)RW`ytF8_e&s=~ITSs8V1-j$Wfr6BO)(}d`AR$k!(v&lYVrIyn^4I#(yCmcJR@#BaJcsN^z zTSxh{l5@xG1&F=~W;cn=aDlU5UQw#O5P%_W�lV<`u0XdUr0v)r!0ulj#>m6Uj00006P)t-s|Ns900033O z(|!N|0NhDLK~#9!tk&xe!ypI+;KTdBbk~MjP%ejLWIvYp>1+iVAcMe2|9rZR2EncJ zGmvc1u5Kqs7(UDdoX>(YmOCB=aVP|*Uk_5N2dk(2@u2mT-yXbfxlcCO%|v?lC0}C{ zm*)e2?>jCNe(dI-9Q#Q9X1-z~_!K_@0t^7cc>`>cnJ2Ui00000NkvXXu0mjfjnjv3 From 8cec6ee207f607c27dc6a638582c350a77694a63 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 04:29:54 +0000 Subject: [PATCH 69/75] Cleaner asset system --- .../settings/xtreme_settings/xtreme_assets.c | 164 ++++++------------ .../settings/xtreme_settings/xtreme_assets.h | 11 -- 2 files changed, 49 insertions(+), 126 deletions(-) diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index b40f272e4..651e680bc 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -4,11 +4,50 @@ XtremeAssets* xtreme_assets = NULL; -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); +void icon(const Icon** replace, const char* name, FuriString* path, File* file) { + furi_string_printf(path, PACKS_DIR "/%s/Icons/%s.bmx", XTREME_SETTINGS()->asset_pack, name); + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint64_t size = storage_file_size(file) - 8; + int32_t width, height; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->frame_count, 1); + FURI_CONST_ASSIGN(icon->frame_rate, 0); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + icon->frames = malloc(sizeof(const uint8_t*)); + FURI_CONST_ASSIGN_PTR(icon->frames[0], malloc(size)); + storage_file_read(file, (void*)icon->frames[0], size); + *replace = icon; + + storage_file_close(file); } - return xtreme_assets; +} + +void swap(XtremeAssets* x, FuriString* p, File* f) { + icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); + icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); + icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); + icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); + icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); + icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); + icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); + icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); + icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); + icon(&x->I_passport_DB, "Passport/passport_DB", p, f); + icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); + icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); + icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); + icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); + icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); + icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); + icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); + icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); + icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); + icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); } void XTREME_ASSETS_LOAD() { @@ -43,126 +82,21 @@ void XTREME_ASSETS_LOAD() { xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; FileInfo info; FuriString* path = furi_string_alloc(); - const char* pack = xtreme_settings->asset_pack; - furi_string_printf(path, PACKS_DIR "/%s", pack); + furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - - swap_bmx_icon( - &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinCommon_56x48, - pack, - "Dolphin/DolphinCommon_56x48.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinMafia_115x62, - pack, - "iButton/DolphinMafia_115x62.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, - pack, - "iButton/iButtonDolphinVerySuccess_108x52.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinReadingSuccess_59x63, - pack, - "Infrared/DolphinReadingSuccess_59x63.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_NFC_dolphin_emulation_47x61, - pack, - "NFC/NFC_dolphin_emulation_47x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_bad_46x49, - pack, - "Passport/passport_bad_46x49.bmx", - path, - file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_passport_happy_46x49, - pack, - "Passport/passport_happy_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_okay_46x49, - pack, - "Passport/passport_okay_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinReceive_97x61, - pack, - "RFID/RFIDDolphinReceive_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSend_97x61, - pack, - "RFID/RFIDDolphinSend_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSuccess_108x57, - pack, - "RFID/RFIDDolphinSuccess_108x57.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); - + swap(xtreme_assets, path, file); storage_file_free(file); } furi_record_close(RECORD_STORAGE); furi_string_free(path); } -void swap_bmx_icon( - const Icon** replace, - const char* pack, - const char* name, - FuriString* path, - File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint64_t size = storage_file_size(file) - 8; - int32_t width, height; - storage_file_read(file, &width, 4); - storage_file_read(file, &height, 4); - - Icon* icon = malloc(sizeof(Icon)); - FURI_CONST_ASSIGN(icon->frame_count, 1); - FURI_CONST_ASSIGN(icon->frame_rate, 0); - FURI_CONST_ASSIGN(icon->width, width); - FURI_CONST_ASSIGN(icon->height, height); - icon->frames = malloc(sizeof(const uint8_t*)); - FURI_CONST_ASSIGN_PTR(icon->frames[0], malloc(size)); - storage_file_read(file, (void*)icon->frames[0], size); - *replace = icon; - - storage_file_close(file); +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); } + return xtreme_assets; } diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 6904a36ab..6a9b48dce 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -32,14 +32,3 @@ typedef struct { } XtremeAssets; XtremeAssets* XTREME_ASSETS(); - -void XTREME_ASSETS_LOAD(); - -void swap_bmx_icon( - const Icon** replace, - const char* base, - const char* name, - FuriString* path, - File* file); - -void free_bmx_icon(Icon* icon); From 1a9f098936beea11964ea71de9cf5316f22a6d2c Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 04:31:03 +0000 Subject: [PATCH 70/75] Move lvlup anim to asset system --- .../desktop/animations/animation_manager.c | 18 ++---------------- .../settings/xtreme_settings/xtreme_assets.c | 1 + .../settings/xtreme_settings/xtreme_assets.h | 1 + .../Animations/Levelup1_128x64/frame_00.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_01.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_02.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_03.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_04.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_05.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_06.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_07.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_08.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_09.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_10.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_11.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_12.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_13.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_14.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_15.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_16.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_17.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_18.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_19.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_20.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_21.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_22.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_23.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_24.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_25.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_26.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_27.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_28.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_29.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_30.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_31.png | Bin 2091 -> 0 bytes .../Animations/Levelup1_128x64/frame_rate | 1 - .../Levelup1_128x64_sfw/frame_00.png | Bin 1326 -> 0 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 1597 -> 0 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 1754 -> 0 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 1828 -> 0 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 1686 -> 0 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 1672 -> 0 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 1659 -> 0 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 1540 -> 0 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 1557 -> 0 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 1551 -> 0 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 1604 -> 0 bytes .../Animations/Levelup2_128x64_sfw/frame_rate | 1 - .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../frame_rate | 0 60 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_10.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_11.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_12.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_13.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_14.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_15.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_16.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_17.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_18.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_19.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_20.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_21.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_22.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_23.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_24.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_25.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_26.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_27.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_28.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_29.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_30.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_31.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_rate delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png delete mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_00.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_01.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_02.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_03.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_04.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_05.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_06.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_07.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_08.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_09.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_10.png (100%) rename assets/icons/Animations/{Levelup1_128x64_sfw => Levelup_128x64}/frame_rate (100%) diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 14e06cdf2..0808a3618 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -569,9 +569,6 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -580,19 +577,8 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_ASSETS()->is_nsfw) { - one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); - } else { - if(stats.level <= 20) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); - } else if(stats.level >= 21) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); - } else { - furi_assert(0); - } - } + one_shot_view_start_animation( + animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 651e680bc..5ae22aab0 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -56,6 +56,7 @@ void XTREME_ASSETS_LOAD() { xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 6a9b48dce..8fa5d1cde 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -8,6 +8,7 @@ typedef struct { bool is_nsfw; + const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; diff --git a/assets/icons/Animations/Levelup1_128x64/frame_00.png b/assets/icons/Animations/Levelup1_128x64/frame_00.png deleted file mode 100644 index a6589b0c7c4d1672fa7a31dfeae856d298b4a865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1071 zcmV+~1kn3ONk%w1VSoTY0J{JH0002R#l`FE>!zlrs;a7si;Kg_CX>@2HWqN06W-eoIZ6HTUMj&lrYh`i` z3IHqs0001h06+i$1pg4oNvplM7Fv1-+Z1}^6@ucIt!(SQ?}>8sESk41!+h_%g~LQa zImk*ijYxP3{+`gNLMR~?kv7S$y3|pl;4o`C^hk`_lxhpGf|vybTTG{uxfN?uX>mK} zb%9|rZWBgFhHW@$S%Qv(9aAKR3WX$aG6!-hjgOw6bYGyOq@|{(sHv)}tgWuEurLRr zv9(YK4wVeHygUaCxdaOV1G~M)7{3k;0|^1o#Ky?TvkVH%!_d$Tw9~Q53Io~11kTS3 z3Jl%ethfyX1KQ*b=EMU7>FeyK?(y>H_p;rCHlTq6efvIH^Y^czLwxEImU2MAp~Z^? z4jP)pQKA?J4F4~Rl!woO$p$D<-tqVZKq;}AI^jKwDm9q`tN{=R7|9gofQkXQcD29&tvms6t&)wm zKFe4WKt}!@*|#BJ8RO?6bmS|2gG5z%F0_Z7+&N;7}{yLYF51 z6?zsiVr>K#cU54unE}&|Pcpt< zq69SXUjIl7yk{SC(U|7|N%aJ-~bUyI9y2G88D1% z%x!p?16U1&p@@(XM+}4}1`uF?8s;a}0pl&j5CI{=NFhN8DX9d{c%`-H00Y(tm_VO} zy{0EU60Qf9n-XzU4NiwbgARNKJdh5kz$kF$q+(JSB%U1tYThsKoHbK`0=7qh0wyT{ zq`rg{3YXij2{5%KU{o18pR6~!2Vs#g<|gg0xoH>Zj^P;UB0=ss_iMN)(b^tS)*|PT zChFevC#|I3sh3~4S|cMg1e|*mMfn=~PdV?7DIc@f)JKf4)-k|L!p6$>tiMv$S?k4L zR%Wk9AYrWUVb5_4*QmwHM72son!zlrs;a7si;D~l43m?S!^6YllnN98N>3M?tJh6zZ;H4 z9Lj7*qY%h!I(-GhNI^0PYekhzdW$~4;IKj}qE@j+(XzZ%lEd&gZ#;F0tm>R@4KTyF z1qys}goJc#C1zb(CVpOng^_VWej`>_mVHQhX_B6kEn_s63Y9c~LI#35ou96+gm193 zw6(UkDGmey1hPE_4hsqghq%T}4hI1N0}DUE3Iqeoy~ox*2Fl9~&c6x+4a?NN*X1M0 z;>!oY3+?R&@Bju2yXorW=GWQwywU^U{|*paa3CDN00;2pGlq`ew1WW}7!=1~0E2N0 z&F!OhG2%mr_Wltd5UyHAYpTTm4H&r%B*>M!5}vfS;9-M*A6-Jc8LrsOTJv)19GY%` z&!4bV)g#JO5}R}nOHy3Bl^!}S(it{#?i%;t681g)J9t1 z6{OpjLivG}3x{pG1b_tyu-9NINW%vQIN%#c0ad|}2iqg5^DM}{jGt%|7g;brX*&(( zoxC{n6Rq|lJ466F@iNPayP0l6_aNZJ30Om+;>+00}LQ=fN{A^lhc0yqM!g-|BTmJb=&>Z00PJnKpp}(i9=z9 zjSwXbbsIvKTZhImZ~%)hE_EV`BwRQY0U);6*?l}7$Kr^aIfvsEDr)FsWVt=)BXZ&d zcchLw5uoIfkTnRwlKVv@+mR(KkR+BHHpnDkfbqyndtj2_0C?va*W!>*5}<%P3Rs6g zTy2_Q0Gzw@xM5&RCa{hJ!SE=Jk$wK@k~CH_$yzjIB3H_T4`jz^o6-F?(sTWo^$n9HgGOF5fP zVW>W!Z@Y7X8ZNvK$=m2O&X%aseyA=}ppW)3oL<1#itr{*6o&+&eT2SCBtZ~IOpmG* zd$pg&Wp0OPyb;ecEXA-X>@Jos;`EeO%Fe3_%Lc0?Xt6a?Ohq=UMhrkQIOqHtj|dLh z;CwinK@hLK)>O1e&5p!cwDgViFwR%CBeXnQ7fl&q1v+?fssc?-z+&+Tt;^InAx4cr z0zqJRb_ImZ@>5^S^9|f0(Wi%-$ifQ=i7h>BoqUGw{SoU1Ox&%0X0Y6I8N$L RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_02.png b/assets/icons/Animations/Levelup1_128x64/frame_02.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_07.png b/assets/icons/Animations/Levelup1_128x64/frame_07.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_08.png b/assets/icons/Animations/Levelup1_128x64/frame_08.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_14.png b/assets/icons/Animations/Levelup1_128x64/frame_14.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_15.png b/assets/icons/Animations/Levelup1_128x64/frame_15.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_19.png b/assets/icons/Animations/Levelup1_128x64/frame_19.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_22.png b/assets/icons/Animations/Levelup1_128x64/frame_22.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_27.png b/assets/icons/Animations/Levelup1_128x64/frame_27.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_28.png b/assets/icons/Animations/Levelup1_128x64/frame_28.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 VH+Qd@rilOm002ovPDHLkV1nhs($D|^ diff --git a/assets/icons/Animations/Levelup1_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64/frame_rate deleted file mode 100644 index c7930257d..000000000 --- a/assets/icons/Animations/Levelup1_128x64/frame_rate +++ /dev/null @@ -1 +0,0 @@ -7 \ No newline at end of file diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png deleted file mode 100644 index bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png deleted file mode 100644 index 39c910d3a41314c985e4ce79337cbbf30d1ae79e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png deleted file mode 100644 index 4975adf8627b62a22d002bfc1d7be423dcc5e83b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png deleted file mode 100644 index e6c88df92b73b89f1ad31810be1a653c3fea5d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png deleted file mode 100644 index e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 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 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png deleted file mode 100644 index 32e864e9827420142930083d480d331024d033f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png deleted file mode 100644 index c692f48952c106373b56ecafd4bce7ba1295f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png deleted file mode 100644 index fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate deleted file mode 100644 index 0cfbf0888..000000000 --- a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png rename to assets/icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png rename to assets/icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png rename to assets/icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png b/assets/icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png rename to assets/icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png rename to assets/icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png rename to assets/icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png b/assets/icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png rename to assets/icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png rename to assets/icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png rename to assets/icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png rename to assets/icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png b/assets/icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png rename to assets/icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64_sfw/frame_rate rename to assets/icons/Animations/Levelup_128x64/frame_rate From 2b7f3797ee10e047dfe8899ddfd2e6fb459c57a4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:53:34 +0000 Subject: [PATCH 71/75] Support animated icons in asset packs --- .../settings/xtreme_settings/xtreme_assets.c | 43 +++++++++++- .../settings/xtreme_settings/xtreme_assets.h | 2 + scripts/asset_packer.py | 70 +++++++++++++------ 3 files changed, 93 insertions(+), 22 deletions(-) diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 5ae22aab0..13014b8d1 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,10 +2,50 @@ #include "assets_icons.h" #include +#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" + XtremeAssets* xtreme_assets = NULL; +void anim(const Icon** replace, const char* name, FuriString* path, File* file) { + do { + furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + int32_t width, height, frame_rate, frame_count; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + storage_file_read(file, &frame_rate, 4); + storage_file_read(file, &frame_count, 4); + storage_file_close(file); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); + FURI_CONST_ASSIGN(icon->frame_count, frame_count); + icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); + const char* pack = XTREME_SETTINGS()->asset_pack; + + bool ok = true; + for(int i = 0; ok && i < icon->frame_count; ++i) { + ok = false; + furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + uint64_t size = storage_file_size(file); + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); + if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; + storage_file_close(file); + } + if(!ok) break; + + *replace = icon; + } while(false); +} + void icon(const Icon** replace, const char* name, FuriString* path, File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s.bmx", XTREME_SETTINGS()->asset_pack, name); + furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -27,6 +67,7 @@ void icon(const Icon** replace, const char* name, FuriString* path, File* file) } void swap(XtremeAssets* x, FuriString* p, File* f) { + anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 8fa5d1cde..038372a43 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -32,4 +32,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; +void XTREME_ASSETS_LOAD(); + XtremeAssets* XTREME_ASSETS(); diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index aea755811..02ed0b94e 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -43,12 +43,51 @@ def convert_bmx(img: "Image.Image | pathlib.Path") -> bytes: return data +def pack_anim(src: pathlib.Path, dst: pathlib.Path): + if not (src / "meta.txt").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "meta.txt": + shutil.copyfile(src / "meta.txt", dst / "meta.txt") + continue + elif frame.name.startswith("frame_"): + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + + +def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): + if not (src / "frame_rate").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + frame_count = 0 + frame_rate = None + size = None + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "frame_rate": + frame_rate = int((src / "frame_rate").read_text()) + continue + elif frame.name.startswith("frame_"): + frame_count += 1 + if not size: + size = Image.open(frame).size + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + (dst / "meta").write_bytes(struct.pack(" Date: Fri, 10 Feb 2023 08:54:20 +0000 Subject: [PATCH 72/75] Load nsfw lvlup from sd with asset pack --- .../Icons/Animations/Levelup_128x64/frame_00.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_01.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_02.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_03.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_04.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_05.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_06.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_07.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_08.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_09.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_10.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_11.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_12.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_13.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_14.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_15.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_16.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_17.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_18.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_19.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_20.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_21.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_22.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_23.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_24.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_25.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_26.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_27.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_28.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_29.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_30.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_31.png | Bin 0 -> 2091 bytes .../Icons/Animations/Levelup_128x64/frame_rate | 1 + .../Icons/Animations/Levelup_128x64/frame_00.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_01.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_02.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_03.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_04.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_05.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_06.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_07.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_08.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_09.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_10.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_11.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_12.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_13.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_14.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_15.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_16.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_17.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_18.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_19.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_20.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_21.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_22.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_23.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_24.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_25.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_26.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_27.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_28.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_29.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_30.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_31.bm | Bin 0 -> 518 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 0 -> 16 bytes 66 files changed, 1 insertion(+) create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..a6589b0c7c4d1672fa7a31dfeae856d298b4a865 GIT binary patch literal 1071 zcmV+~1kn3ONk%w1VSoTY0J{JH0002R#l`FE>!zlrs;a7si;Kg_CX>@2HWqN06W-eoIZ6HTUMj&lrYh`i` z3IHqs0001h06+i$1pg4oNvplM7Fv1-+Z1}^6@ucIt!(SQ?}>8sESk41!+h_%g~LQa zImk*ijYxP3{+`gNLMR~?kv7S$y3|pl;4o`C^hk`_lxhpGf|vybTTG{uxfN?uX>mK} zb%9|rZWBgFhHW@$S%Qv(9aAKR3WX$aG6!-hjgOw6bYGyOq@|{(sHv)}tgWuEurLRr zv9(YK4wVeHygUaCxdaOV1G~M)7{3k;0|^1o#Ky?TvkVH%!_d$Tw9~Q53Io~11kTS3 z3Jl%ethfyX1KQ*b=EMU7>FeyK?(y>H_p;rCHlTq6efvIH^Y^czLwxEImU2MAp~Z^? z4jP)pQKA?J4F4~Rl!woO$p$D<-tqVZKq;}AI^jKwDm9q`tN{=R7|9gofQkXQcD29&tvms6t&)wm zKFe4WKt}!@*|#BJ8RO?6bmS|2gG5z%F0_Z7+&N;7}{yLYF51 z6?zsiVr>K#cU54unE}&|Pcpt< zq69SXUjIl7yk{SC(U|7|N%aJ-~bUyI9y2G88D1% z%x!p?16U1&p@@(XM+}4}1`uF?8s;a}0pl&j5CI{=NFhN8DX9d{c%`-H00Y(tm_VO} zy{0EU60Qf9n-XzU4NiwbgARNKJdh5kz$kF$q+(JSB%U1tYThsKoHbK`0=7qh0wyT{ zq`rg{3YXij2{5%KU{o18pR6~!2Vs#g<|gg0xoH>Zj^P;UB0=ss_iMN)(b^tS)*|PT zChFevC#|I3sh3~4S|cMg1e|*mMfn=~PdV?7DIc@f)JKf4)-k|L!p6$>tiMv$S?k4L zR%Wk9AYrWUVb5_4*QmwHM72son!zlrs;a7si;D~l43m?S!^6YllnN98N>3M?tJh6zZ;H4 z9Lj7*qY%h!I(-GhNI^0PYekhzdW$~4;IKj}qE@j+(XzZ%lEd&gZ#;F0tm>R@4KTyF z1qys}goJc#C1zb(CVpOng^_VWej`>_mVHQhX_B6kEn_s63Y9c~LI#35ou96+gm193 zw6(UkDGmey1hPE_4hsqghq%T}4hI1N0}DUE3Iqeoy~ox*2Fl9~&c6x+4a?NN*X1M0 z;>!oY3+?R&@Bju2yXorW=GWQwywU^U{|*paa3CDN00;2pGlq`ew1WW}7!=1~0E2N0 z&F!OhG2%mr_Wltd5UyHAYpTTm4H&r%B*>M!5}vfS;9-M*A6-Jc8LrsOTJv)19GY%` z&!4bV)g#JO5}R}nOHy3Bl^!}S(it{#?i%;t681g)J9t1 z6{OpjLivG}3x{pG1b_tyu-9NINW%vQIN%#c0ad|}2iqg5^DM}{jGt%|7g;brX*&(( zoxC{n6Rq|lJ466F@iNPayP0l6_aNZJ30Om+;>+00}LQ=fN{A^lhc0yqM!g-|BTmJb=&>Z00PJnKpp}(i9=z9 zjSwXbbsIvKTZhImZ~%)hE_EV`BwRQY0U);6*?l}7$Kr^aIfvsEDr)FsWVt=)BXZ&d zcchLw5uoIfkTnRwlKVv@+mR(KkR+BHHpnDkfbqyndtj2_0C?va*W!>*5}<%P3Rs6g zTy2_Q0Gzw@xM5&RCa{hJ!SE=Jk$wK@k~CH_$yzjIB3H_T4`jz^o6-F?(sTWo^$n9HgGOF5fP zVW>W!Z@Y7X8ZNvK$=m2O&X%aseyA=}ppW)3oL<1#itr{*6o&+&eT2SCBtZ~IOpmG* zd$pg&Wp0OPyb;ecEXA-X>@Jos;`EeO%Fe3_%Lc0?Xt6a?Ohq=UMhrkQIOqHtj|dLh z;CwinK@hLK)>O1e&5p!cwDgViFwR%CBeXnQ7fl&q1v+?fssc?-z+&+Tt;^InAx4cr z0zqJRb_ImZ@>5^S^9|f0(Wi%-$ifQ=i7h>BoqUGw{SoU1Ox&%0X0Y6I8N$L RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 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 RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 VH+Qd@rilOm002ovPDHLkV1nhs($D|^ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate new file mode 100644 index 000000000..c7930257d --- /dev/null +++ b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta new file mode 100644 index 0000000000000000000000000000000000000000..485a421b6e4585f9e7e41e4cc628e815074fdb04 GIT binary patch literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 literal 0 HcmV?d00001 From 0a43a72fc17e937f09537101a8d6b177f5f70e61 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:55:47 +0000 Subject: [PATCH 73/75] Init asset packs at desktop load --- applications/services/desktop/desktop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 7840cd00a..b5b73668b 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#include "../../settings/xtreme_settings/xtreme_assets.h" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -305,6 +307,9 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + // TODO: find a (working) way to run this at startup without hooking desktop + XTREME_ASSETS_LOAD(); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { From a1d1e2a5a20865980dda61e3aa763758e2694b15 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:56:07 +0000 Subject: [PATCH 74/75] Format --- applications/plugins/nightstand/application.fam | 1 - applications/plugins/pomodoro/application.fam | 2 +- applications/plugins/scrambler/application.fam | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index caec3cbf6..cea02bd00 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,4 +10,3 @@ App( fap_category="Misc", order=81, ) - diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 27e73a0ce..750373342 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) \ No newline at end of file +) diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 8d87a4a62..4d48d7bb5 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP From 9b80cec2fa20e9e09e67fc55f7d529cd21b33b42 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 10:09:28 +0000 Subject: [PATCH 75/75] Add internal flash browser tab --- applications/main/archive/helpers/archive_browser.c | 12 ++++++++---- applications/main/archive/helpers/archive_browser.h | 2 ++ .../main/archive/views/archive_browser_view.c | 1 + .../main/archive/views/archive_browser_view.h | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 3163b8cff..9cb66a5e5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,10 +457,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; + break; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 5b13e98da..43a9a651a 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index dce753fde..26ed17d75 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a525a7db6..6e6582405 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,6 +31,7 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum;